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] 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: { }