diff --git a/.circleci/config.yml b/.circleci/config.yml index f84aed3235..925adf6b24 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,8 +9,8 @@ executors: - image: docker.mirror.hashicorp.services/circleci/golang:1.17 environment: TEST_RESULTS: /tmp/test-results # path to where test results are saved - CONSUL_VERSION: 1.11.0-beta3 # Consul's OSS version to use in tests - CONSUL_ENT_VERSION: 1.11.0+ent-beta3 # Consul's enterprise version to use in tests + CONSUL_VERSION: 1.11.0-rc # Consul's OSS version to use in tests + CONSUL_ENT_VERSION: 1.11.0+ent-rc # Consul's enterprise version to use in tests control-plane-path : &control-plane-path control-plane cli-path : &cli-path cli diff --git a/CHANGELOG.md b/CHANGELOG.md index 1165ade1f4..02bd5de687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,52 @@ ## UNRELEASED +## 0.38.0 (December 08, 2021) + BREAKING CHANGES: -* Update minimum go version for project to 1.17 [[GH-878](https://github.com/hashicorp/consul-k8s/pull/878)] +* Control Plane + * Update minimum go version for project to 1.17 [[GH-878](https://github.com/hashicorp/consul-k8s/pull/878)] + * Add boolean metric to merged metrics response `consul_merged_service_metrics_success` to indicate if service metrics + were scraped successfully. [[GH-551](https://github.com/hashicorp/consul-k8s/pull/551)] + +FEATURES: +* Vault as a Secrets Backend: Add support for Vault as a secrets backend for Gossip Encryption, Server TLS certs and Service Mesh TLS certificates, + removing the existing usage of Kubernetes Secrets for the respective secrets. [[GH-904](https://github.com/hashicorp/consul-k8s/pull/904/)] + + See the [Consul Kubernetes and Vault documentation](https://www.consul.io/docs/k8s/installation/vault) + for full install instructions. + + Requirements: + * Consul 1.11+ + * Vault 1.19+ and Vault-K8s 0.14+ must be installed with the Vault Agent Injector enabled (`injector.enabled=true`) + into the Kubernetes cluster that Consul is installed into. + * `global.tls.enableAutoEncryption=true` is required for TLS support. + * If TLS is enabled in Vault, `global.secretsBackend.vault.ca` must be provided and should reference a Kube secret + which holds a copy of the Vault CA cert. + * Add boolean metric to merged metrics response `consul_merged_service_metrics_success` to indicate if service metrics were + scraped successfully. [[GH-551](https://github.com/hashicorp/consul-k8s/pull/551)] +* Helm + * Rename `PartitionExports` CRD to `ExportedServices`. [[GH-902](https://github.com/hashicorp/consul-k8s/pull/902)] + +FEATURES: +* CLI + * **BETA** Add `upgrade` command to modify Consul installation on Kubernetes. [[GH-898](https://github.com/hashicorp/consul-k8s/pull/898)] IMPROVEMENTS: * CLI - * Pre-check in the `install` command to verify the correct license secret exists when using an enterprise Consul image. [[GH-875](https://github.com/hashicorp/consul-k8s/pull/875)] + * Pre-check in the `install` command to verify the correct license secret exists when using an enterprise Consul image. [[GH-875](https://github.com/hashicorp/consul-k8s/pull/875)] * Control Plane - * Add a label "managed-by" to every secret the control-plane creates. Only delete said secrets on an uninstall. [[GH-835](https://github.com/hashicorp/consul-k8s/pull/835)] + * Add a label "managed-by" to every secret the control-plane creates. Only delete said secrets on an uninstall. [[GH-835](https://github.com/hashicorp/consul-k8s/pull/835)] * Add support for labeling a Kubernetes service with `consul.hashicorp.com/service-ignore` to prevent services from being registered in Consul. [[GH-858](https://github.com/hashicorp/consul-k8s/pull/858)] +* Helm Chart + * Fail an installation/upgrade if WAN federation and Admin Partitions are both enabled. [[GH-892](https://github.com/hashicorp/consul-k8s/issues/892)] + * Add support for setting `ingressClassName` for UI. [[GH-909](https://github.com/hashicorp/consul-k8s/pull/909)] + * Add partition support to Service Resolver, Service Router and Service Splitter CRDs. [[GH-908](https://github.com/hashicorp/consul-k8s/issues/908)] BUG FIXES: * Control Plane: - * Add a workaround to check that the ACL token is replicated to other Consul servers. [[GH-862](https://github.com/hashicorp/consul-k8s/issues/862)] - -BUG FIXES: + * Add a workaround to check that the ACL token is replicated to other Consul servers. [[GH-862](https://github.com/hashicorp/consul-k8s/issues/862)] + * Return 500 on prometheus response if unable to get metrics from Envoy. [[GH-551](https://github.com/hashicorp/consul-k8s/pull/551)] + * Don't include body of failed service metrics calls in merged metrics response. [[GH-551](https://github.com/hashicorp/consul-k8s/pull/551)] * Helm Chart * Admin Partitions **(Consul Enterprise only)**: Do not mount Consul CA certs to partition-init job if `externalServers.useSystemRoots` is `true`. [[GH-885](https://github.com/hashicorp/consul-k8s/pull/885)] diff --git a/acceptance/framework/consul/consul_cluster.go b/acceptance/framework/consul/consul_cluster.go index d34024b865..90b5dc392b 100644 --- a/acceptance/framework/consul/consul_cluster.go +++ b/acceptance/framework/consul/consul_cluster.go @@ -2,7 +2,6 @@ package consul import ( "context" - "encoding/json" "fmt" "strings" "testing" @@ -115,7 +114,7 @@ func (h *HelmCluster) Create(t *testing.T) { }) // Fail if there are any existing installations of the Helm chart. - h.checkForPriorInstallations(t) + helpers.CheckForPriorInstallations(t, h.kubernetesClient, h.helmOptions, "consul-helm", "chart=consul-helm") helm.Install(t, h.helmOptions, config.HelmChartPath, h.releaseName) @@ -281,49 +280,6 @@ func (h *HelmCluster) SetupConsulClient(t *testing.T, secure bool) *api.Client { return consulClient } -// checkForPriorInstallations checks if there is an existing Helm release -// for this Helm chart already installed. If there is, it fails the tests. -func (h *HelmCluster) checkForPriorInstallations(t *testing.T) { - t.Helper() - - var helmListOutput string - // 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) { - 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 - // get used to fail the test, just for logging. - helmListOutput, err = helm.RunHelmCommandAndGetOutputE(t, h.helmOptions, "list", "--output", "json") - require.NoError(r, err) - }) - - var installedReleases []map[string]string - - err := json.Unmarshal([]byte(helmListOutput), &installedReleases) - require.NoError(t, err, "unmarshalling %q", helmListOutput) - - for _, r := range installedReleases { - require.NotContains(t, r["chart"], "consul", fmt.Sprintf("detected an existing installation of Consul %s, release name: %s", r["chart"], r["name"])) - } - - // 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) { - consulPods, err := h.kubernetesClient.CoreV1().Pods(h.helmOptions.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{}) - require.NoError(r, err) - if len(consulPods.Items) > 0 { - var podNames []string - for _, p := range consulPods.Items { - podNames = append(podNames, p.Name) - } - r.Errorf("pods from previous installation still running: %s", strings.Join(podNames, ", ")) - } - }) - -} - // configurePodSecurityPolicies creates a simple pod security policy, a cluster role to allow access to the PSP, // and a role binding that binds the default service account in the helm installation namespace to the cluster role. // We bind the default service account for tests that are spinning up pods without a service account set so that diff --git a/acceptance/framework/helpers/helpers.go b/acceptance/framework/helpers/helpers.go index 5869176bbb..bbf02bcbaf 100644 --- a/acceptance/framework/helpers/helpers.go +++ b/acceptance/framework/helpers/helpers.go @@ -2,6 +2,7 @@ package helpers import ( "context" + "encoding/json" "fmt" "os" "os/signal" @@ -10,6 +11,8 @@ import ( "testing" "time" + "github.com/gruntwork-io/terratest/modules/helm" + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/gruntwork-io/terratest/modules/random" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -25,6 +28,48 @@ func RandomName() string { return fmt.Sprintf("test-%s", strings.ToLower(random.UniqueId())) } +// CheckForPriorInstallations checks if there is an existing Helm release +// for this Helm chart already installed. If there is, it fails the tests. +func CheckForPriorInstallations(t *testing.T, client kubernetes.Interface, options *helm.Options, chartName, labelSelector string) { + t.Helper() + + var helmListOutput string + // 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) { + 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 + // get used to fail the test, just for logging. + helmListOutput, err = helm.RunHelmCommandAndGetOutputE(t, options, "list", "--output", "json") + require.NoError(r, err) + }) + + var installedReleases []map[string]string + + err := json.Unmarshal([]byte(helmListOutput), &installedReleases) + require.NoError(t, err, "unmarshalling %q", helmListOutput) + + for _, r := range installedReleases { + require.NotContains(t, r["chart"], chartName, fmt.Sprintf("detected an existing installation of %s %s, release name: %s", chartName, r["chart"], r["name"])) + } + + // 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) { + pods, err := client.CoreV1().Pods(options.KubectlOptions.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector}) + require.NoError(r, err) + if len(pods.Items) > 0 { + var podNames []string + for _, p := range pods.Items { + podNames = append(podNames, p.Name) + } + r.Errorf("pods from previous installation still running: %s", strings.Join(podNames, ", ")) + } + }) +} + // WaitForAllPodsToBeReady waits until all pods with the provided podLabelSelector // are in the ready status. It checks every 5 seconds for a total of 20 tries. // If there is at least one container in a pod that isn't ready after that, @@ -32,7 +77,7 @@ func RandomName() string { func WaitForAllPodsToBeReady(t *testing.T, client kubernetes.Interface, namespace, podLabelSelector string) { t.Helper() - logger.Log(t, "Waiting for pods to be ready.") + logger.Logf(t, "Waiting for pods with label %q to be ready.", podLabelSelector) // Wait up to 10m. // On Azure, volume provisioning can sometimes take close to 5 min, @@ -41,6 +86,7 @@ func WaitForAllPodsToBeReady(t *testing.T, client kubernetes.Interface, namespac 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) + require.NotEmpty(r, pods.Items) var notReadyPods []string for _, pod := range pods.Items { @@ -55,7 +101,7 @@ func WaitForAllPodsToBeReady(t *testing.T, client kubernetes.Interface, namespac logger.Log(t, "Finished waiting for pods to be ready.") } -// Sets up a goroutine that will wait for interrupt signals +// SetupInterruptHandler sets up a goroutine that will wait for interrupt signals // and call cleanup function when it catches it. func SetupInterruptHandler(cleanup func()) { c := make(chan os.Signal, 1) diff --git a/acceptance/framework/k8s/deploy.go b/acceptance/framework/k8s/deploy.go index 6616e6f45c..2cf5e8876a 100644 --- a/acceptance/framework/k8s/deploy.go +++ b/acceptance/framework/k8s/deploy.go @@ -134,6 +134,7 @@ func CheckStaticServerConnectionFailing(t *testing.T, options *k8s.KubectlOption CheckStaticServerConnection(t, options, false, []string{ "curl: (52) Empty reply from server", "curl: (7) Failed to connect", + "curl: (56) Recv failure: Connection reset by peer", }, curlArgs...) } diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go new file mode 100644 index 0000000000..bab5fbb753 --- /dev/null +++ b/acceptance/framework/vault/vault_cluster.go @@ -0,0 +1,368 @@ +package vault + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/gruntwork-io/terratest/modules/helm" + 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/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/helper/cert" + "github.com/hashicorp/consul/sdk/testutil/retry" + vapi "github.com/hashicorp/vault/api" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +const ( + releaseLabel = "app.kubernetes.io/instance=" +) + +// VaultCluster represents a vault installation. +type VaultCluster struct { + ctx environment.TestContext + + helmOptions *helm.Options + releaseName string + vaultClient *vapi.Client + + kubectlOptions *terratestk8s.KubectlOptions + + kubernetesClient kubernetes.Interface + + noCleanupOnFailure bool + debugDirectory string + logger terratestLogger.TestLogger +} + +// NewVaultCluster creates a VaultCluster which will be used to install Vault using Helm. +func NewVaultCluster(t *testing.T, ctx environment.TestContext, cfg *config.TestConfig, releaseName string) *VaultCluster { + + logger := terratestLogger.New(logger.TestLogger{}) + + kopts := ctx.KubectlOptions(t) + + vaultHelmOpts := &helm.Options{ + SetValues: defaultHelmValues(releaseName), + KubectlOptions: kopts, + Logger: logger, + } + if cfg.EnablePodSecurityPolicies { + vaultHelmOpts.SetValues["global.psp.enable"] = "true" + } + helm.AddRepo(t, vaultHelmOpts, "hashicorp", "https://helm.releases.hashicorp.com") + // Ignoring the error from `helm repo update` as it could fail due to stale cache or unreachable servers and we're + // asserting a chart version on Install which would fail in an obvious way should this not succeed. + _, err := helm.RunHelmCommandAndGetOutputE(t, &helm.Options{}, "repo", "update") + if err != nil { + logger.Logf(t, "Unable to update helm repository, proceeding anyway: %s.", err) + } + + return &VaultCluster{ + ctx: ctx, + helmOptions: vaultHelmOpts, + kubectlOptions: kopts, + kubernetesClient: ctx.KubernetesClient(t), + noCleanupOnFailure: cfg.NoCleanupOnFailure, + debugDirectory: cfg.DebugDirectory, + logger: logger, + releaseName: releaseName, + } +} + +// VaultClient returns the vault client. +func (v *VaultCluster) VaultClient(t *testing.T) *vapi.Client { return v.vaultClient } + +// SetupVaultClient sets up and returns a Vault Client. +func (v *VaultCluster) SetupVaultClient(t *testing.T) *vapi.Client { + t.Helper() + + if v.vaultClient != nil { + return v.vaultClient + } + + config := vapi.DefaultConfig() + localPort := terratestk8s.GetAvailablePort(t) + remotePort := 8200 // use non-secure by default + + serverPod := fmt.Sprintf("%s-vault-0", v.releaseName) + tunnel := terratestk8s.NewTunnelWithLogger( + v.helmOptions.KubectlOptions, + terratestk8s.ResourceTypePod, + serverPod, + localPort, + remotePort, + 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) { + // 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)) + }) + + t.Cleanup(func() { + tunnel.Close() + }) + + config.Address = fmt.Sprintf("https://127.0.0.1:%d", localPort) + // We don't need to verify TLS for localhost traffic. + err := config.ConfigureTLS(&vapi.TLSConfig{Insecure: true}) + require.NoError(t, err) + vaultClient, err := vapi.NewClient(config) + require.NoError(t, err) + return vaultClient +} + +// bootstrap sets up Kubernetes auth method and enables secrets engines. +func (v *VaultCluster) bootstrap(t *testing.T, ctx environment.TestContext) { + + v.vaultClient = v.SetupVaultClient(t) + + // Enable the KV-V2 Secrets engine. + err := v.vaultClient.Sys().Mount("consul", &vapi.MountInput{ + Type: "kv-v2", + Config: vapi.MountConfigInput{}, + }) + require.NoError(t, err) + + // Enable the PKI Secrets engine. + err = v.vaultClient.Sys().Mount("pki", &vapi.MountInput{ + Type: "pki", + Config: vapi.MountConfigInput{}, + }) + require.NoError(t, err) + + // Enable Kube Auth. + err = v.vaultClient.Sys().EnableAuthWithOptions("kubernetes", &vapi.EnableAuthOptions{ + Type: "kubernetes", + }) + require.NoError(t, err) + + v.logger.Logf(t, "updating vault kube auth config") + + // To configure the auth method, we need to read the token and the ca cert from the Vault's server + // service account token. + namespace := v.helmOptions.KubectlOptions.Namespace + sa, err := v.kubernetesClient.CoreV1().ServiceAccounts(namespace).Get(context.Background(), fmt.Sprintf("%s-vault", v.releaseName), metav1.GetOptions{}) + require.NoError(t, err) + require.Len(t, sa.Secrets, 1) + tokenSecret, err := v.kubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), sa.Secrets[0].Name, metav1.GetOptions{}) + require.NoError(t, err) + _, err = v.vaultClient.Logical().Write("auth/kubernetes/config", map[string]interface{}{ + "token_reviewer_jwt": tokenSecret.StringData["token"], + "kubernetes_ca_cert": tokenSecret.StringData["ca.crt"], + "kubernetes_host": "https://kubernetes.default.svc", + }) + require.NoError(t, err) +} + +// Create installs Vault via Helm and then calls bootstrap to initialize it. +func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext) { + t.Helper() + + // 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() { + v.Destroy(t) + }) + + // Fail if there are any existing installations of the Helm chart. + helpers.CheckForPriorInstallations(t, v.kubernetesClient, v.helmOptions, "", v.releaseLabelSelector()) + + v.createTLSCerts(t) + + // Install Vault. + helm.Install(t, v.helmOptions, "hashicorp/vault", v.releaseName) + + v.initAndUnseal(t) + + // Wait for the injector and vault server pods to become Ready. + helpers.WaitForAllPodsToBeReady(t, v.kubernetesClient, v.helmOptions.KubectlOptions.Namespace, v.releaseLabelSelector()) + + // Now call bootstrap(). + v.bootstrap(t, ctx) +} + +// Destroy issues a helm delete and deletes the PVC + any helm secrets related to the release that are leftover. +func (v *VaultCluster) Destroy(t *testing.T) { + t.Helper() + + k8s.WritePodsDebugInfoIfFailed(t, v.kubectlOptions, v.debugDirectory, v.releaseLabelSelector()) + // Ignore the error returned by the helm delete here so that we can + // always idempotently clean up resources in the cluster. + _ = helm.DeleteE(t, v.helmOptions, v.releaseName, true) + + err := v.kubernetesClient.CoreV1().PersistentVolumeClaims(v.helmOptions.KubectlOptions.Namespace).DeleteCollection(context.Background(), + metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: v.releaseLabelSelector()}) + require.NoError(t, err) +} + +func defaultHelmValues(releaseName string) map[string]string { + certSecret := certSecretName(releaseName) + caSecret := CASecretName(releaseName) + + serverConfig := fmt.Sprintf(` + listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + tls_cert_file = "/vault/userconfig/%s/tls.crt" + tls_key_file = "/vault/userconfig/%s/tls.key" + tls_client_ca_file = "/vault/userconfig/%s/tls.crt" + } + + storage "file" { + path = "/vault/data" + }`, certSecret, certSecret, caSecret) + + return map[string]string{ + "global.tlsDisable": "false", + "server.extraEnvironmentVars.VAULT_CACERT": fmt.Sprintf("/vault/userconfig/%s/tls.crt", caSecret), + "server.extraVolumes[0].name": caSecret, + "server.extraVolumes[0].type": "secret", + "server.extraVolumes[1].name": certSecret, + "server.extraVolumes[1].type": "secret", + "server.standalone.enabled": "true", + "server.standalone.config": serverConfig, + "injector.enabled": "true", + "ui.enabled": "true", + } +} + +// certSecretName returns the Kubernetes secret name of the certificate and key +// for the Vault server. +func certSecretName(releaseName string) string { + return fmt.Sprintf("%s-vault-server-tls", releaseName) +} + +// CASecretName returns the Kubernetes secret name of the CA for the Vault server. +func CASecretName(releaseName string) string { + return fmt.Sprintf("%s-vault-ca", releaseName) +} + +// Address is the in-cluster API address of the Vault server. +func (v *VaultCluster) Address() string { + return fmt.Sprintf("https://%s-vault:8200", v.releaseName) +} + +// releaseLabelSelector returns label selector that selects all pods +// from a Vault installation. +func (v *VaultCluster) releaseLabelSelector() string { + return fmt.Sprintf("%s=%s", releaseLabel, v.releaseName) +} + +// createTLSCerts generates a self-signed CA and uses it to generate +// certificate and key for the Vault server. It then saves those as +// Kubernetes secrets. +func (v *VaultCluster) createTLSCerts(t *testing.T) { + v.logger.Logf(t, "generating Vault TLS certificates") + + namespace := v.helmOptions.KubectlOptions.Namespace + + // Generate CA and cert and create secrets for them. + signer, _, caPem, caCertTmpl, err := cert.GenerateCA("Vault CA") + require.NoError(t, err) + vaultService := fmt.Sprintf("%s-vault", v.releaseName) + certSANs := []string{ + vaultService, + fmt.Sprintf("%s.default", vaultService), + fmt.Sprintf("%s.default.svc", vaultService), + } + certPem, keyPem, err := cert.GenerateCert("Vault server", 24*time.Hour, caCertTmpl, signer, certSANs) + require.NoError(t, err) + + t.Cleanup(func() { + 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{}) + } + }) + + _, err = v.kubernetesClient.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: certSecretName(v.releaseName), + }, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte(certPem), + corev1.TLSPrivateKeyKey: []byte(keyPem), + }, + Type: corev1.SecretTypeTLS, + }, metav1.CreateOptions{}) + require.NoError(t, err) + + _, err = v.kubernetesClient.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: CASecretName(v.releaseName), + Namespace: namespace, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte(caPem), + }, + Type: corev1.SecretTypeOpaque, + }, metav1.CreateOptions{}) + require.NoError(t, err) +} + +// initAndUnseal initializes and unseals Vault. +// Once initialized, it saves the Vault root token into a Kubernetes secret. +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} + 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) + + // Set up the client so that we can make API calls to initialize and unseal. + v.vaultClient = v.SetupVaultClient(t) + + // Initialize Vault with 1 secret share. We don't need to + // more key shares for this test installation. + initResp, err := v.vaultClient.Sys().Init(&vapi.InitRequest{ + SecretShares: 1, + SecretThreshold: 1, + }) + require.NoError(r, err) + v.vaultClient.SetToken(initResp.RootToken) + + // Unseal Vault with the unseal key we got when initialized it. + // There should be one unseal key since we're only using one secret share. + _, err = v.vaultClient.Sys().Unseal(initResp.KeysB64[0]) + require.NoError(r, err) + }) + + v.logger.Logf(t, "successfully initialized and unsealed Vault") + + 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() { + _ = v.kubernetesClient.CoreV1().Secrets(namespace).Delete(context.Background(), rootTokenSecret, metav1.DeleteOptions{}) + }) + _, err := v.kubernetesClient.CoreV1().Secrets(namespace).Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: rootTokenSecret, + Namespace: namespace, + }, + Data: map[string][]byte{ + "token": []byte(v.vaultClient.Token()), + }, + Type: corev1.SecretTypeOpaque, + }, metav1.CreateOptions{}) + require.NoError(t, err) +} diff --git a/acceptance/go.mod b/acceptance/go.mod index 8a22b898e7..db655d76b3 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -4,8 +4,10 @@ go 1.17 require ( github.com/gruntwork-io/terratest v0.31.2 - github.com/hashicorp/consul/api v1.10.1-0.20211116182834-e6956893fb6f + github.com/hashicorp/consul-k8s/control-plane v0.0.0-20211207212234-aea9efea5638 + github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc github.com/hashicorp/consul/sdk v0.8.0 + github.com/hashicorp/vault/api v1.2.0 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.22.2 @@ -15,57 +17,82 @@ require ( require ( cloud.google.com/go v0.54.0 // indirect - github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect - github.com/aws/aws-sdk-go v1.27.1 // indirect + github.com/armon/go-metrics v0.3.9 // indirect + github.com/armon/go-radix v1.0.0 // 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/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v4.11.0+incompatible // indirect - github.com/fatih/color v1.9.0 // indirect + github.com/fatih/color v1.12.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-sql-driver/mysql v1.4.1 // 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/google/go-cmp v0.5.5 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.6 // 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/gruntwork-io/gruntwork-cli v0.7.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-hclog v0.12.0 // indirect - github.com/hashicorp/go-immutable-radix 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-hclog v0.16.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.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/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/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.0 // indirect github.com/hashicorp/golang-lru v0.5.3 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/serf v0.9.6 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // 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.3.0 // indirect github.com/json-iterator/go v1.1.11 // indirect - github.com/mattn/go-colorable v0.1.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/mitchellh/go-testing-interface v1.14.0 // 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.1 // indirect + 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/pquerna/otp v1.2.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // 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-20210220033148-5ea612d1eb83 // indirect golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - google.golang.org/appengine v1.6.5 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/grpc v1.38.0 // indirect google.golang.org/protobuf v1.26.0 // 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.0-20210107192922-496545a6307b // indirect k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 1c81c79d35..c082349208 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= @@ -62,39 +63,68 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 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/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= 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/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/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 h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= 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.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= +github.com/armon/go-metrics v0.3.9/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/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.27.1 h1:MXnqY6SlWySaZAqNnXThOvjRFdiiOuKtC6i7baFdNdU= +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/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/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= 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/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +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/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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 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= @@ -104,7 +134,6 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= @@ -123,6 +152,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= @@ -138,23 +168,35 @@ 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/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.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 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 h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.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 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 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/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.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/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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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= @@ -162,7 +204,11 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 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-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-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= @@ -178,9 +224,14 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8 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-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 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-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= @@ -212,8 +263,13 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/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= @@ -222,8 +278,11 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a 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= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.0/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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= @@ -250,6 +309,7 @@ github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9 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= @@ -261,57 +321,107 @@ 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/api v1.10.1-0.20211116182834-e6956893fb6f h1:fBBh4412td7nBzqyLkpGTH5dWycPs8p7Yg/Dy8VQjVU= -github.com/hashicorp/consul/api v1.10.1-0.20211116182834-e6956893fb6f/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20211207212234-aea9efea5638 h1:z68s6H6O3RjxDmNvou/2/3UBrsJkrMcNzI0IQN5scAM= +github.com/hashicorp/consul-k8s/control-plane v0.0.0-20211207212234-aea9efea5638/go.mod h1:7ZeaiADGbvJDuoWAT8UKj6KCcLsFUk+34OkUGMVtdXg= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc h1:tUgL1cinAFDtidyKqgsJzlxLkEi9atLmN6j8kgCr17Q= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc h1:tUgL1cinAFDtidyKqgsJzlxLkEi9atLmN6j8kgCr17Q= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc h1:tUgL1cinAFDtidyKqgsJzlxLkEi9atLmN6j8kgCr17Q= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -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/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +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-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= +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-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +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-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +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/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 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= 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-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-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +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-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 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 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-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.2.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.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/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/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc= +github.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +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/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= 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/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= -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.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/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= 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 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -327,6 +437,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/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= @@ -339,15 +451,17 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 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 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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.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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 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-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= @@ -357,15 +471,28 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju 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/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 v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 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/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= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -380,9 +507,14 @@ 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/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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +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= 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= @@ -392,20 +524,32 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +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 h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= 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= @@ -418,29 +562,40 @@ 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_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/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/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/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/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/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 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/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/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= @@ -448,10 +603,12 @@ 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= @@ -468,9 +625,13 @@ 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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= @@ -481,6 +642,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/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= 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= @@ -488,12 +650,17 @@ 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.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/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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= @@ -501,6 +668,7 @@ 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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= @@ -529,6 +697,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-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= @@ -537,11 +706,13 @@ 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.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= 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= @@ -566,9 +737,15 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/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-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -594,14 +771,17 @@ 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= @@ -609,6 +789,7 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w 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= @@ -629,14 +810,24 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/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-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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= +golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/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= @@ -647,12 +838,14 @@ 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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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 h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -671,6 +864,7 @@ 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= @@ -678,6 +872,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= @@ -697,7 +892,10 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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= @@ -721,8 +919,9 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -742,16 +941,26 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/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/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= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 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= @@ -761,9 +970,14 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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= @@ -774,11 +988,15 @@ 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 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= @@ -786,6 +1004,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -795,6 +1014,7 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C 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= 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= @@ -803,9 +1023,13 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/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.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= +k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= +k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= 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.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= @@ -823,7 +1047,6 @@ k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 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 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= 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= @@ -847,7 +1070,8 @@ 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/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 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= +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 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= diff --git a/acceptance/tests/controller/controller_namespaces_test.go b/acceptance/tests/controller/controller_namespaces_test.go index d68b2c5414..b272247db4 100644 --- a/acceptance/tests/controller/controller_namespaces_test.go +++ b/acceptance/tests/controller/controller_namespaces_test.go @@ -74,7 +74,7 @@ func TestControllerNamespaces(t *testing.T) { ctx := suite.Environment().DefaultContext(t) helmValues := map[string]string{ - "global.image": "hashicorp/consul-enterprise:1.11.0-ent-beta3", + "global.image": "hashicorp/consul-enterprise:1.11.0-ent-rc", "global.enableConsulNamespaces": "true", "global.adminPartitions.enabled": "true", @@ -156,12 +156,12 @@ func TestControllerNamespaces(t *testing.T) { require.True(r, ok, "could not cast to ProxyConfigEntry") require.Equal(r, api.MeshGatewayModeLocal, proxyDefaultEntry.MeshGateway.Mode) - // partition-exports - entry, _, err = consulClient.ConfigEntries().Get(api.PartitionExports, "default", defaultOpts) + // exported-services + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) require.NoError(r, err) - partitionExportsEntry, ok := entry.(*api.PartitionExportsConfigEntry) - require.True(r, ok, "could not cast to PartitionExportsConfigEntry") - require.Equal(r, "frontend", partitionExportsEntry.Services[0].Name) + 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) @@ -232,7 +232,7 @@ func TestControllerNamespaces(t *testing.T) { logger.Log(t, "patching partition-exports custom resource") patchServiceName := "backend" - k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "partitionexports", "default", "-p", fmt.Sprintf(`{"spec":{"services":[{"name": "%s", "namespace": "front", "consumers":[{"partition": "foo"}]}]}}`, patchServiceName), "--type=merge") + 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") @@ -279,11 +279,11 @@ func TestControllerNamespaces(t *testing.T) { require.Equal(r, api.MeshGatewayModeRemote, proxyDefaultsEntry.MeshGateway.Mode) // partition-exports - entry, _, err = consulClient.ConfigEntries().Get(api.PartitionExports, "default", defaultOpts) + entry, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) require.NoError(r, err) - partitionExportsEntry, ok := entry.(*api.PartitionExportsConfigEntry) - require.True(r, ok, "could not cast to PartitionExportsConfigEntry") - require.Equal(r, "backend", partitionExportsEntry.Services[0].Name) + 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) @@ -343,7 +343,7 @@ func TestControllerNamespaces(t *testing.T) { 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, "partitionexports", "default") + 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") @@ -381,7 +381,7 @@ func TestControllerNamespaces(t *testing.T) { require.Contains(r, err.Error(), "404 (Config entry not found") // partition-exports - _, _, err = consulClient.ConfigEntries().Get(api.PartitionExports, "default", defaultOpts) + _, _, err = consulClient.ConfigEntries().Get(api.ExportedServices, "default", defaultOpts) require.Error(r, err) require.Contains(r, err.Error(), "404 (Config entry not found") diff --git a/acceptance/tests/fixtures/bases/admin-partitions/kustomization.yaml b/acceptance/tests/fixtures/bases/admin-partitions/kustomization.yaml deleted file mode 100644 index 04a88496e8..0000000000 --- a/acceptance/tests/fixtures/bases/admin-partitions/kustomization.yaml +++ /dev/null @@ -1,3 +0,0 @@ -resources: - - partitionexports-default.yaml - - partitionexports-secondary.yaml diff --git a/acceptance/tests/fixtures/bases/admin-partitions/partitionexports-default.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml similarity index 79% rename from acceptance/tests/fixtures/bases/admin-partitions/partitionexports-default.yaml rename to acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml index 718b5eae5e..a260602109 100644 --- a/acceptance/tests/fixtures/bases/admin-partitions/partitionexports-default.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-default/exportedservices-default.yaml @@ -1,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: default spec: diff --git a/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml new file mode 100644 index 0000000000..e540a4def1 --- /dev/null +++ b/acceptance/tests/fixtures/bases/exportedservices-default/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - exportedservices-default.yaml diff --git a/acceptance/tests/fixtures/bases/admin-partitions/partitionexports-secondary.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml similarity index 79% rename from acceptance/tests/fixtures/bases/admin-partitions/partitionexports-secondary.yaml rename to acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml index 2f09fe87be..a514ed50d9 100644 --- a/acceptance/tests/fixtures/bases/admin-partitions/partitionexports-secondary.yaml +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/exportedservices-secondary.yaml @@ -1,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: secondary spec: diff --git a/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml new file mode 100644 index 0000000000..10af8e20c5 --- /dev/null +++ b/acceptance/tests/fixtures/bases/exportedservices-secondary/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - exportedservices-secondary.yaml diff --git a/acceptance/tests/fixtures/bases/intention/intention.yaml b/acceptance/tests/fixtures/bases/intention/intention.yaml new file mode 100644 index 0000000000..c7bf26dac2 --- /dev/null +++ b/acceptance/tests/fixtures/bases/intention/intention.yaml @@ -0,0 +1,10 @@ +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions +metadata: + name: client-to-server +spec: + destination: + name: static-server + sources: + - name: static-client + action: allow diff --git a/acceptance/tests/fixtures/bases/intention/kustomization.yaml b/acceptance/tests/fixtures/bases/intention/kustomization.yaml new file mode 100644 index 0000000000..8d15c05511 --- /dev/null +++ b/acceptance/tests/fixtures/bases/intention/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - intention.yaml \ 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 ccb255ba30..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,5 +1,5 @@ resources: - - ../../../bases/admin-partitions + - ../../../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 5e836da962..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,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: default spec: 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 ccb255ba30..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,5 +1,5 @@ resources: - - ../../../bases/admin-partitions + - ../../../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 dda2d4bfde..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,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: default spec: 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 ccb255ba30..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,5 +1,5 @@ resources: - - ../../../bases/admin-partitions + - ../../../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 73ac31c6a8..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,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: secondary spec: 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 ccb255ba30..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,5 +1,5 @@ resources: - - ../../../bases/admin-partitions + - ../../../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 2e1b399797..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,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: secondary spec: diff --git a/acceptance/tests/fixtures/cases/crds-ent/partitionexports.yaml b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml similarity index 87% rename from acceptance/tests/fixtures/cases/crds-ent/partitionexports.yaml rename to acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml index cbcf65c389..4703d23493 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/partitionexports.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/exportedservices.yaml @@ -1,5 +1,5 @@ apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExports +kind: ExportedServices metadata: name: default spec: diff --git a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml index 75ca5cd845..cdc3e60cb1 100644 --- a/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml +++ b/acceptance/tests/fixtures/cases/crds-ent/kustomization.yaml @@ -1,3 +1,3 @@ resources: - ../../bases/crds-oss - - partitionexports.yaml + - exportedservices.yaml diff --git a/acceptance/tests/metrics/metrics_test.go b/acceptance/tests/metrics/metrics_test.go index 25e480c8d3..0ad3ce22c4 100644 --- a/acceptance/tests/metrics/metrics_test.go +++ b/acceptance/tests/metrics/metrics_test.go @@ -3,7 +3,9 @@ package metrics import ( "context" "fmt" + "github.com/hashicorp/consul/sdk/testutil/retry" "testing" + "time" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" @@ -121,12 +123,17 @@ func TestAppMetrics(t *testing.T) { require.NoError(t, err) require.Len(t, podList.Items, 1) podIP := podList.Items[0].Status.PodIP - metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+staticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP)) - require.NoError(t, err) - // This assertion represents the metrics from the envoy sidecar. - require.Contains(t, metricsOutput, `envoy_cluster_assignment_stale{local_cluster="server",consul_source_service="server"`) - // This assertion represents the metrics from the application. - require.Contains(t, metricsOutput, `service_started_total 1`) + + // 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) { + 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. + require.Contains(r, metricsOutput, `envoy_cluster_assignment_stale{local_cluster="server",consul_source_service="server"`) + // This assertion represents the metrics from the application. + require.Contains(r, metricsOutput, `service_started_total 1`) + }) } func assertGatewayMetricsEnabled(t *testing.T, ctx environment.TestContext, ns, label, metricsAssertion string) { diff --git a/acceptance/tests/partitions/partitions_test.go b/acceptance/tests/partitions/partitions_test.go index a9c04a3c5e..357e2f6d85 100644 --- a/acceptance/tests/partitions/partitions_test.go +++ b/acceptance/tests/partitions/partitions_test.go @@ -37,7 +37,7 @@ func TestPartitions(t *testing.T) { } if cfg.EnableTransparentProxy { - t.Skipf("skipping this test because -enable-transparent-proxy is true") + t.Skipf("skipping this test as Transparent Proxy behavior is flaky") } const defaultPartition = "default" @@ -96,7 +96,7 @@ func TestPartitions(t *testing.T) { serverHelmValues := map[string]string{ "global.datacenter": "dc1", - "global.image": "hashicorp/consul-enterprise:1.11.0-ent-beta3", + "global.image": "hashicorp/consul-enterprise:1.11.0-ent-rc", "global.adminPartitions.enabled": "true", "global.enableConsulNamespaces": "true", @@ -110,7 +110,6 @@ func TestPartitions(t *testing.T) { // When mirroringK8S is set, this setting is ignored. "connectInject.consulNamespaces.consulDestinationNamespace": c.destinationNamespace, "connectInject.consulNamespaces.mirroringK8S": strconv.FormatBool(c.mirrorK8S), - "connectInject.transparentProxy.defaultEnabled": "false", "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), @@ -118,6 +117,8 @@ func TestPartitions(t *testing.T) { "meshGateway.replicas": "1", "controller.enabled": "true", + + "dns.enabled": "true", } if cfg.UseKind { @@ -127,6 +128,10 @@ func TestPartitions(t *testing.T) { serverHelmValues["meshGateway.service.nodePort"] = "30100" } + if cfg.EnableTransparentProxy { + serverHelmValues["dns.enableRedirection"] = "true" + } + releaseName := helpers.RandomName() // Install the consul cluster with servers in the default kubernetes context. @@ -192,7 +197,7 @@ func TestPartitions(t *testing.T) { // Create client cluster. clientHelmValues := map[string]string{ "global.datacenter": "dc1", - "global.image": "hashicorp/consul-enterprise:1.11.0-ent-beta3", + "global.image": "hashicorp/consul-enterprise:1.11.0-ent-rc", "global.enabled": "false", "global.tls.enabled": "true", @@ -205,7 +210,6 @@ func TestPartitions(t *testing.T) { // When mirroringK8S is set, this setting is ignored. "connectInject.consulNamespaces.consulDestinationNamespace": c.destinationNamespace, "connectInject.consulNamespaces.mirroringK8S": strconv.FormatBool(c.mirrorK8S), - "connectInject.transparentProxy.defaultEnabled": "false", "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), @@ -228,6 +232,8 @@ func TestPartitions(t *testing.T) { "client.enabled": "true", "client.exposeGossipPorts": "true", "client.join[0]": partitionSvcIP, + + "dns.enabled": "true", } if c.secure { @@ -247,6 +253,10 @@ func TestPartitions(t *testing.T) { clientHelmValues["meshGateway.service.nodePort"] = "30100" } + if cfg.EnableTransparentProxy { + clientHelmValues["dns.enableRedirection"] = "true" + } + // Install the consul cluster without servers in the client cluster kubernetes context. clientConsulCluster := consul.NewHelmCluster(t, clientHelmValues, clientClusterContext, cfg, releaseName) clientConsulCluster.Create(t) @@ -364,17 +374,25 @@ func TestPartitions(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, serverClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + if cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + if c.destinationNamespace == defaultNamespace { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + } else { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, 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, clientClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + if cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-namespaces") + if c.destinationNamespace == defaultNamespace { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + } else { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, 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. for _, labelSelector := range []string{"app=static-server", "app=static-client"} { @@ -422,8 +440,13 @@ func TestPartitions(t *testing.T) { if c.secure { logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, "http://localhost:1234") - k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, "http://localhost:1234") + if cfg.EnableTransparentProxy { + k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + } else { + k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, "http://localhost:1234") + k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, "http://localhost:1234") + } intention := &api.ServiceIntentionsConfigEntry{ Kind: api.ServiceIntentions, @@ -450,11 +473,22 @@ func TestPartitions(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() { + _, 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}) + require.NoError(t, err) + }) } logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, "http://localhost:1234") - k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, "http://localhost:1234") + if cfg.EnableTransparentProxy { + k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + } else { + k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, "http://localhost:1234") + } // Test that kubernetes readiness status is synced to Consul. // Create the file so that the readiness probe of the static-server pod fails. @@ -468,25 +502,38 @@ func TestPartitions(t *testing.T) { // there will be no healthy proxy host to connect to. That's why we can't assert that we receive an empty reply // from server, which is the case when a connection is unsuccessful due to intentions in other tests. logger.Log(t, "checking that connection is unsuccessful") - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + if cfg.EnableTransparentProxy { + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.%s", staticServerNamespace)) + } else { + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + } }) // This section of the tests run the cross-partition networking tests. 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, serverClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") + if cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-partition") + if c.destinationNamespace == defaultNamespace { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-partition") + } else { + k8s.DeployKustomize(t, serverClusterStaticClientOpts, 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, clientClusterStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.destinationNamespace == defaultNamespace { - k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") + if cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") } else { - k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/ns-default-partition") + if c.destinationNamespace == defaultNamespace { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-partitions/default-ns-default-partition") + } else { + k8s.DeployKustomize(t, clientClusterStaticClientOpts, 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. for _, labelSelector := range []string{"app=static-server", "app=static-client"} { @@ -535,7 +582,7 @@ func TestPartitions(t *testing.T) { require.NoError(t, err) require.Len(t, services, 1) - logger.Log(t, "creating partition exports") + logger.Log(t, "creating exported services") if c.destinationNamespace == defaultNamespace { k8s.KubectlApplyK(t, serverClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/default-partition-default") k8s.KubectlApplyK(t, clientClusterContext.KubectlOptions(t), "../fixtures/cases/crd-partitions/secondary-partition-default") @@ -554,8 +601,18 @@ func TestPartitions(t *testing.T) { if c.secure { logger.Log(t, "checking that the connection is not successful because there's no intention") - k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, "http://localhost:1234") - k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, "http://localhost:1234") + if cfg.EnableTransparentProxy { + if !c.mirrorK8S { + k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, defaultPartition)) + } else { + k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, defaultPartition)) + } + } else { + k8s.CheckStaticServerConnectionFailing(t, serverClusterStaticClientOpts, "http://localhost:1234") + k8s.CheckStaticServerConnectionFailing(t, clientClusterStaticClientOpts, "http://localhost:1234") + } intention := &api.ServiceIntentionsConfigEntry{ Name: staticServerName, @@ -584,11 +641,27 @@ func TestPartitions(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() { + _, 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}) + require.NoError(t, err) + }) } logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, "http://localhost:1234") - k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, "http://localhost:1234") + if cfg.EnableTransparentProxy { + if !c.mirrorK8S { + k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, defaultPartition)) + } else { + k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, defaultPartition)) + } + } else { + k8s.CheckStaticServerConnectionSuccessful(t, serverClusterStaticClientOpts, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, clientClusterStaticClientOpts, "http://localhost:1234") + } // Test that kubernetes readiness status is synced to Consul. // Create the file so that the readiness probe of the static-server pod fails. @@ -602,8 +675,18 @@ func TestPartitions(t *testing.T) { // there will be no healthy proxy host to connect to. That's why we can't assert that we receive an empty reply // from server, which is the case when a connection is unsuccessful due to intentions in other tests. logger.Log(t, "checking that connection is unsuccessful") - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + if cfg.EnableTransparentProxy { + if !c.mirrorK8S { + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", c.destinationNamespace, defaultPartition)) + } else { + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, secondaryPartition)) + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, fmt.Sprintf("http://static-server.virtual.%s.ns.%s.ap.dc1.dc.consul", staticServerNamespace, defaultPartition)) + } + } else { + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, serverClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, clientClusterStaticClientOpts, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server"}, "http://localhost:1234") + } }) }) } diff --git a/acceptance/tests/vault/main_test.go b/acceptance/tests/vault/main_test.go new file mode 100644 index 0000000000..1d3a5a5842 --- /dev/null +++ b/acceptance/tests/vault/main_test.go @@ -0,0 +1,15 @@ +package vault + +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/vault/vault_test.go b/acceptance/tests/vault/vault_test.go new file mode 100644 index 0000000000..e19e196c84 --- /dev/null +++ b/acceptance/tests/vault/vault_test.go @@ -0,0 +1,269 @@ +package vault + +import ( + "crypto/rand" + "encoding/base64" + "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/acceptance/framework/vault" + "github.com/stretchr/testify/require" +) + +const ( + gossipPolicy = ` +path "consul/data/secret/gossip" { + capabilities = ["read"] +}` + + // connectCAPolicy allows Consul to bootstrap all certificates for the service mesh in Vault. + // Adapted from https://www.consul.io/docs/connect/ca/vault#consul-managed-pki-paths. + connectCAPolicy = ` +path "/sys/mounts" { + capabilities = [ "read" ] +} + +path "/sys/mounts/connect_root" { + capabilities = [ "create", "read", "update", "delete", "list" ] +} + +path "/sys/mounts/connect_inter" { + capabilities = [ "create", "read", "update", "delete", "list" ] +} + +path "/connect_root/*" { + capabilities = [ "create", "read", "update", "delete", "list" ] +} + +path "/connect_inter/*" { + capabilities = [ "create", "read", "update", "delete", "list" ] +} +` + serverTLSPolicy = ` +path "pki/issue/consul-server" { + capabilities = ["create", "update"] +}` + caPolicy = ` +path "pki/cert/ca" { + capabilities = ["read"] +}` +) + +// 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) { + cfg := suite.Config() + ctx := suite.Environment().DefaultContext(t) + ns := ctx.KubectlOptions(t).Namespace + + consulReleaseName := helpers.RandomName() + vaultReleaseName := helpers.RandomName() + consulClientServiceAccountName := fmt.Sprintf("%s-consul-client", consulReleaseName) + consulServerServiceAccountName := fmt.Sprintf("%s-consul-server", consulReleaseName) + + vaultCluster := vault.NewVaultCluster(t, ctx, cfg, vaultReleaseName) + vaultCluster.Create(t, ctx) + // Vault is now installed in the cluster. + + // Now fetch the Vault client so we can create the policies and secrets. + vaultClient := vaultCluster.VaultClient(t) + + // Create the Vault Policy for the gossip key. + logger.Log(t, "Creating policies") + err := vaultClient.Sys().PutPolicy("consul-gossip", gossipPolicy) + require.NoError(t, err) + + // Create the Vault Policy for the connect-ca. + err = vaultClient.Sys().PutPolicy("connect-ca", connectCAPolicy) + require.NoError(t, err) + + // Create the Auth Roles for consul-server and consul-client. + // Auth roles bind policies to Kubernetes service accounts, which + // then enables the Vault agent init container to call 'vault login' + // with the Kubernetes auth method to obtain a Vault token. + // Please see https://www.vaultproject.io/docs/auth/kubernetes#configuration + // for more details. + logger.Log(t, "Creating the consul-server and consul-client roles") + params := map[string]interface{}{ + "bound_service_account_names": consulClientServiceAccountName, + "bound_service_account_namespaces": ns, + "policies": "consul-gossip", + "ttl": "24h", + } + _, err = vaultClient.Logical().Write("auth/kubernetes/role/consul-client", params) + require.NoError(t, err) + + params = map[string]interface{}{ + "bound_service_account_names": consulServerServiceAccountName, + "bound_service_account_namespaces": ns, + "policies": "consul-gossip,connect-ca,consul-server", + "ttl": "24h", + } + _, err = vaultClient.Logical().Write("auth/kubernetes/role/consul-server", params) + require.NoError(t, err) + + // Create the CA role that all components will use to fetch the Server CA certs. + params = map[string]interface{}{ + "bound_service_account_names": "*", + "bound_service_account_namespaces": ns, + "policies": "consul-ca", + "ttl": "24h", + } + _, err = vaultClient.Logical().Write("auth/kubernetes/role/consul-ca", params) + require.NoError(t, err) + + // Generate the gossip secret. + gossipKey, err := generateGossipSecret() + require.NoError(t, err) + + // Create the gossip secret. + logger.Log(t, "Creating the gossip secret") + params = map[string]interface{}{ + "data": map[string]interface{}{ + "gossip": gossipKey, + }, + } + _, err = vaultClient.Logical().Write("consul/data/secret/gossip", params) + require.NoError(t, err) + + vaultCASecret := vault.CASecretName(vaultReleaseName) + + // Bootstrap TLS by creating the CA infrastructure required for Consul server TLS and also create the `consul-server` PKI role. + // Using https://learn.hashicorp.com/tutorials/consul/vault-pki-consul-secure-tls. + // Generate the root CA. + params = map[string]interface{}{ + "common_name": "dc1.consul", + "ttl": "24h", + } + _, err = vaultClient.Logical().Write("pki/root/generate/internal", params) + require.NoError(t, err) + + // Create the Vault PKI Role. + name := consulReleaseName + "-consul" + allowedDomains := fmt.Sprintf("dc1.consul,%s-server,%s-server.%s,%s-server.%s.svc", name, name, ns, name, ns) + params = map[string]interface{}{ + "allowed_domains": allowedDomains, + "allow_bare_domains": "true", + "allow_localhost": "true", + "allow_subdomains": "true", + "generate_lease": "true", + "max_ttl": "1h", + } + _, err = vaultClient.Logical().Write("pki/roles/consul-server", params) + require.NoError(t, err) + + // Create the server and ca policies + err = vaultClient.Sys().PutPolicy("consul-server", serverTLSPolicy) + require.NoError(t, err) + err = vaultClient.Sys().PutPolicy("consul-ca", caPolicy) + require.NoError(t, err) + + consulHelmValues := map[string]string{ + // TODO: Update the global image once 1.11 is GA. + "global.image": "docker.mirror.hashicorp.services/hashicorpdev/consul:latest", + + "server.enabled": "true", + "server.replicas": "1", + "server.extraVolumes[0].type": "secret", + "server.extraVolumes[0].name": vaultCASecret, + "server.extraVolumes[0].load": "false", + "global.datacenter": "dc1", + + "connectInject.enabled": "true", + "connectInject.replicas": "1", + "controller.enabled": "true", + + "global.secretsBackend.vault.enabled": "true", + "global.secretsBackend.vault.consulServerRole": "consul-server", + "global.secretsBackend.vault.consulClientRole": "consul-client", + "global.secretsBackend.vault.consulCARole": "consul-ca", + + "global.secretsBackend.vault.ca.secretName": vaultCASecret, + "global.secretsBackend.vault.ca.secretKey": "tls.crt", + + "global.secretsBackend.vault.connectCA.address": vaultCluster.Address(), + "global.secretsBackend.vault.connectCA.rootPKIPath": "connect_root", + "global.secretsBackend.vault.connectCA.intermediatePKIPath": "connect_inter", + + "global.acls.manageSystemACLs": "true", + "global.tls.enabled": "true", + "global.gossipEncryption.secretName": "consul/data/secret/gossip", + "global.gossipEncryption.secretKey": "gossip", + + "ingressGateways.enabled": "true", + "ingressGateways.defaults.replicas": "1", + "terminatingGateways.enabled": "true", + "terminatingGateways.defaults.replicas": "1", + + "server.serverCert.secretName": "pki/issue/consul-server", + "global.tls.caCert.secretName": "pki/cert/ca", + "global.tls.httpsOnly": "false", + "global.tls.enableAutoEncrypt": "true", + + // For sync catalog, it is sufficient to check that the deployment is running and ready + // because we only care that get-auto-encrypt-client-ca init container was able + // to talk to the Consul server using the CA from Vault. For this reason, + // we don't need any services to be synced in either direction. + "syncCatalog.enabled": "true", + "syncCatalog.toConsul": "false", + "syncCatalog.toK8S": "false", + } + logger.Log(t, "Installing Consul") + consulCluster := consul.NewHelmCluster(t, consulHelmValues, ctx, cfg, consulReleaseName) + consulCluster.Create(t) + + // Validate that the gossip encryption key is set correctly. + logger.Log(t, "Validating the gossip key has been set correctly.") + consulClient := consulCluster.SetupConsulClient(t, true) + keys, err := consulClient.Operator().KeyringList(nil) + require.NoError(t, err) + // There are two identical keys for LAN and WAN since there is only 1 dc. + require.Len(t, keys, 2) + require.Equal(t, 1, keys[0].PrimaryKeys[gossipKey]) + + // Confirm that the Vault Connect CA has been bootstrapped correctly. + caConfig, _, err := consulClient.Connect().CAGetConfig(nil) + require.NoError(t, err) + require.Equal(t, caConfig.Provider, "vault") + + // 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") + if cfg.EnableTransparentProxy { + 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.DebugDirectory, "../fixtures/cases/static-client-inject") + } + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") + }) + k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention") + + logger.Log(t, "checking that connection is successful") + if cfg.EnableTransparentProxy { + k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), "http://static-server") + } else { + k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), "http://localhost:1234") + } +} + +// generateGossipSecret generates a random 32 byte secret returned as a base64 encoded string. +func generateGossipSecret() (string, error) { + // This code was copied from Consul's Keygen command: + // https://github.com/hashicorp/consul/blob/d652cc86e3d0322102c2b5e9026c6a60f36c17a5/command/keygen/keygen.go + + key := make([]byte, 32) + n, err := rand.Reader.Read(key) + if err != nil { + return "", fmt.Errorf("error reading random data: %s", err) + } + if n != 32 { + return "", fmt.Errorf("couldn't read enough entropy") + } + + return base64.StdEncoding.EncodeToString(key), nil +} diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 740f90d1fd..0057b0780b 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: consul -version: 0.37.0 +version: 0.38.0 appVersion: 1.10.4 kubeVersion: ">=1.17.0-0" description: Official HashiCorp Consul Chart @@ -15,7 +15,7 @@ annotations: - name: consul image: hashicorp/consul:1.10.4 - name: consul-k8s-control-plane - image: hashicorp/consul-k8s-control-plane:0.37.0 + image: hashicorp/consul-k8s-control-plane:0.38.0 - name: envoy image: envoyproxy/envoy-alpine:v1.18.4 artifacthub.io/license: MPL-2.0 diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index 480c4b8895..42b49f0aa1 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -15,6 +15,42 @@ as well as the global.name setting. {{- end -}} {{- end -}} +{{- define "consul.vaultGossipTemplate" -}} + | + {{ "{{" }}- with secret "{{ .secretName }}" -{{ "}}" }} + {{ "{{" }}- {{ printf ".Data.data.%s" .secretKey }} -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + +{{- define "consul.serverTLSCATemplate" -}} + | + {{ "{{" }}- with secret "{{ .Values.global.tls.caCert.secretName }}" -{{ "}}" }} + {{ "{{" }}- .Data.certificate -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + +{{- define "consul.serverTLSCertTemplate" -}} + | + {{ "{{" }}- with secret "{{ .Values.server.serverCert.secretName }}" "{{ printf "common_name=server.%s.%s" .Values.global.datacenter .Values.global.domain }}" + "ttl=1h" "alt_names={{ include "consul.serverTLSAltNames" . }}" "ip_sans=127.0.0.1" -{{ "}}" }} + {{ "{{" }}- .Data.certificate -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + +{{- define "consul.serverTLSKeyTemplate" -}} + | + {{ "{{" }}- with secret "{{ .Values.server.serverCert.secretName }}" "{{ printf "common_name=server.%s.%s" .Values.global.datacenter .Values.global.domain }}" + "ttl=1h" "alt_names={{ include "consul.serverTLSAltNames" . }}" "ip_sans=127.0.0.1" -{{ "}}" }} + {{ "{{" }}- .Data.private_key -{{ "}}" }} + {{ "{{" }}- end -{{ "}}" }} +{{- end -}} + +{{- define "consul.serverTLSAltNames" -}} +{{- $name := include "consul.fullname" . -}} +{{- $ns := .Release.Namespace -}} +{{ printf "localhost,%s-server,*.%s-server,*.%s-server.%s,*.%s-server.%s.svc,*.server.%s.%s" $name $name $name $ns $name $ns (.Values.global.datacenter ) (.Values.global.domain) }} +{{- end -}} + {{/* Sets up the extra-from-values config file passed to consul and then uses sed to do any necessary substitution for HOST_IP/POD_IP/HOSTNAME. Useful for dogstats telemetry. The output file @@ -114,13 +150,19 @@ This template is for an init container. {{- else }} -server-addr={{ template "consul.fullname" . }}-server \ -server-port=8501 \ + {{- if .Values.global.secretsBackend.vault.enabled }} + -ca-file=/vault/secrets/serverca.crt + {{- else }} -ca-file=/consul/tls/ca/tls.crt {{- end }} + {{- end }} volumeMounts: {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca {{- end }} + {{- end }} - name: consul-auto-encrypt-ca-cert mountPath: /consul/tls/client/ca resources: diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index c98265480d..9517d8d974 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -2,6 +2,11 @@ {{- if (and (and .Values.global.tls.enabled .Values.global.tls.httpsOnly) (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics))}}{{ fail "global.metrics.enableAgentMetrics cannot be enabled if TLS (HTTPS only) is enabled" }}{{ end -}} {{- $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 $serverEnabled (ne .Values.global.adminPartitions.name "default"))}}{{ fail "global.adminPartitions.name has to be \"default\" in the server cluster" }}{{ end -}} +{{- if (and (not .Values.global.secretsBackend.vault.consulClientRole) .Values.global.secretsBackend.vault.enabled) }}{{ fail "global.secretsBackend.vault.consulClientRole must be provided if global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.caCert.secretName)) }}{{ fail "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.enableAutoEncrypt)) }}{{ fail "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.secretsBackend.vault.consulCARole)) }}{{ fail "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- 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 }} # DaemonSet to run the Consul clients on every node. apiVersion: apps/v1 kind: DaemonSet @@ -38,6 +43,24 @@ spec: {{- toYaml .Values.client.extraLabels | nindent 8 }} {{- end }} annotations: + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": "{{ .Values.global.secretsBackend.vault.consulClientRole }}" + {{- 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.gossipEncryption.secretName }} + {{- with .Values.global.gossipEncryption }} + "vault.hashicorp.com/agent-inject-secret-gossip.txt": {{ .secretName }} + "vault.hashicorp.com/agent-inject-template-gossip.txt": {{ template "consul.vaultGossipTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- end }} + {{- end }} "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/client-config-configmap.yaml") . | sha256sum }} {{- if .Values.client.annotations }} @@ -90,6 +113,7 @@ spec: configMap: name: {{ template "consul.fullname" . }}-client-config {{- if .Values.global.tls.enabled }} + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -118,6 +142,7 @@ spec: medium: "Memory" {{- end }} {{- end }} + {{- end }} {{- range .Values.client.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -170,6 +195,7 @@ spec: - name: CONSUL_DISABLE_PERM_MGMT value: "true" {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: GOSSIP_KEY valueFrom: secretKeyRef: @@ -181,6 +207,7 @@ spec: key: {{ .Values.global.gossipEncryption.secretKey }} {{- end }} {{- end }} + {{- end }} {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.acls.manageSystemACLs)) }} - name: CONSUL_LICENSE_PATH value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} @@ -203,6 +230,9 @@ spec: - | CONSUL_FULLNAME="{{template "consul.fullname" . }}" + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} + GOSSIP_KEY=`cat /vault/secrets/gossip.txt` + {{- end }} {{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }} {{ template "consul.recursors" }} {{- end }} @@ -219,7 +249,11 @@ spec: {{- end }} -hcl='leave_on_terminate = true' \ {{- if .Values.global.tls.enabled }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -hcl='ca_file = "/vault/secrets/serverca.crt"' \ + {{- else }} -hcl='ca_file = "/consul/tls/ca/tls.crt"' \ + {{- end }} {{- if .Values.global.tls.enableAutoEncrypt }} -hcl='auto_encrypt = {tls = true}' \ -hcl="auto_encrypt = {ip_san = [\"$HOST_IP\",\"$POD_IP\"]}" \ @@ -291,6 +325,7 @@ spec: - name: config mountPath: /consul/config {{- if .Values.global.tls.enabled }} + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca readOnly: true @@ -300,6 +335,7 @@ spec: readOnly: true {{- end }} {{- end }} + {{- end }} {{- range .Values.client.extraVolumes }} - name: userconfig-{{ .name }} readOnly: true @@ -433,6 +469,7 @@ spec: mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0.pem tls.crt mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0-key.pem tls.key volumeMounts: + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: consul-client-cert mountPath: /consul/tls/client - name: consul-ca-cert @@ -441,6 +478,7 @@ spec: - name: consul-ca-key mountPath: /consul/tls/ca/key readOnly: true + {{- end }} resources: requests: memory: "50Mi" diff --git a/charts/consul/templates/client-snapshot-agent-deployment.yaml b/charts/consul/templates/client-snapshot-agent-deployment.yaml index 80fa46dfc9..e9377b49e9 100644 --- a/charts/consul/templates/client-snapshot-agent-deployment.yaml +++ b/charts/consul/templates/client-snapshot-agent-deployment.yaml @@ -28,6 +28,17 @@ spec: component: client-snapshot-agent annotations: "consul.hashicorp.com/connect-inject": "false" + {{- 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 }} + {{- end }} spec: {{- if .Values.client.tolerations }} tolerations: diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 11946b6235..854d6f13e0 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -38,6 +38,17 @@ spec: component: connect-injector annotations: "consul.hashicorp.com/connect-inject": "false" + {{- 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 }} + {{- end }} spec: serviceAccountName: {{ template "consul.fullname" . }}-connect-injector-webhook-svc-account containers: diff --git a/charts/consul/templates/controller-clusterrole.yaml b/charts/consul/templates/controller-clusterrole.yaml index 993768811c..45fa8d8458 100644 --- a/charts/consul/templates/controller-clusterrole.yaml +++ b/charts/consul/templates/controller-clusterrole.yaml @@ -17,7 +17,7 @@ rules: - serviceresolvers - proxydefaults - meshes - - partitionexports + - exportedservices - servicerouters - servicesplitters - serviceintentions @@ -38,7 +38,7 @@ rules: - serviceresolvers/status - proxydefaults/status - meshes/status - - partitionexports/status + - exportedservices/status - servicerouters/status - servicesplitters/status - serviceintentions/status diff --git a/charts/consul/templates/controller-deployment.yaml b/charts/consul/templates/controller-deployment.yaml index 151c08b1e8..5caf5e6a48 100644 --- a/charts/consul/templates/controller-deployment.yaml +++ b/charts/consul/templates/controller-deployment.yaml @@ -30,6 +30,17 @@ spec: component: controller annotations: "consul.hashicorp.com/connect-inject": "false" + {{- 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 }} + {{- end }} spec: {{- if or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt) }} initContainers: diff --git a/charts/consul/templates/controller-mutatingwebhookconfiguration.yaml b/charts/consul/templates/controller-mutatingwebhookconfiguration.yaml index 7bef2e6c27..03f4ce15c4 100644 --- a/charts/consul/templates/controller-mutatingwebhookconfiguration.yaml +++ b/charts/consul/templates/controller-mutatingwebhookconfiguration.yaml @@ -200,4 +200,25 @@ webhooks: resources: - terminatinggateways sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-exportedservices + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-exportedservices.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - exportedservices + sideEffects: None {{- end }} diff --git a/charts/consul/templates/crd-partitionexports.yaml b/charts/consul/templates/crd-exportedservices.yaml similarity index 93% rename from charts/consul/templates/crd-partitionexports.yaml rename to charts/consul/templates/crd-exportedservices.yaml index a39dcaad0a..67f67e5704 100644 --- a/charts/consul/templates/crd-partitionexports.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -6,7 +6,7 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.0 creationTimestamp: null - name: partitionexports.consul.hashicorp.com + name: exportedservices.consul.hashicorp.com labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} @@ -16,10 +16,10 @@ metadata: spec: group: consul.hashicorp.com names: - kind: PartitionExports - listKind: PartitionExportsList - plural: partitionexports - singular: partitionexports + kind: ExportedServices + listKind: ExportedServicesList + plural: exportedservices + singular: exportedservices scope: Namespaced versions: - additionalPrinterColumns: @@ -38,7 +38,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PartitionExports is the Schema for the partitionexports API + description: ExportedServices is the Schema for the exportedservices API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -53,7 +53,7 @@ spec: metadata: type: object spec: - description: PartitionExportsSpec defines the desired state of PartitionExports + description: ExportedServicesSpec defines the desired state of ExportedServices properties: services: description: Services is a list of services to be exported and the diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index 998bc0e6e0..2bf85cbf0a 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -186,8 +186,14 @@ spec: from instead of the current one. type: string namespace: - description: Namespace is the namespace to resolve the service - from instead of the current one. + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. type: string service: description: Service is a service to resolve instead of the current diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index ac8905ca2c..ac0f515b8e 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -76,6 +76,11 @@ spec: the request when a retryable result occurs format: int32 type: integer + partition: + description: Partition is the Consul partition to resolve + the service from instead of the current partition. If + empty the current partition is assumed. + type: string prefixRewrite: description: PrefixRewrite defines how to rewrite the HTTP request path before proxying it to its final destination. diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index f7bb3e5246..e91b16db6c 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -62,9 +62,14 @@ spec: items: properties: namespace: - description: The namespace to resolve the service from instead - of the current namespace. If empty the current namespace is - assumed. + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. type: string requestHeaders: description: Allow HTTP header manipulation to be configured. diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 36671fb2b8..5b8c8dc4b7 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -55,6 +55,17 @@ spec: component: ingress-gateway ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} annotations: + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- end }} "consul.hashicorp.com/connect-inject": "false" {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} "prometheus.io/scrape": "true" diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 1fb43cbd02..95758c5c77 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -34,12 +34,23 @@ spec: component: server-acl-init annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-pre-populate-only": "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 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init {{- if (or .Values.global.tls.enabled (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey)) }} volumes: - {{- if .Values.global.tls.enabled }} + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -77,7 +88,7 @@ spec: fieldPath: metadata.namespace {{- if (or .Values.global.tls.enabled (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey)) }} volumeMounts: - {{- if .Values.global.tls.enabled }} + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} - name: consul-ca-cert mountPath: /consul/tls/ca readOnly: true @@ -119,8 +130,12 @@ spec: {{- if .Values.global.tls.enabled }} -use-https \ {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -consul-ca-cert=/vault/secrets/serverca.crt \ + {{- else }} -consul-ca-cert=/consul/tls/ca/tls.crt \ {{- end }} + {{- end }} {{- if not .Values.externalServers.enabled }} -server-port=8501 \ {{- end }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index 83b31755fc..f11177089a 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -12,6 +12,39 @@ metadata: release: {{ .Release.Name }} component: server data: + {{- $vaultConnectCAEnabled := and .Values.global.secretsBackend.vault.connectCA.address .Values.global.secretsBackend.vault.connectCA.rootPKIPath .Values.global.secretsBackend.vault.connectCA.intermediatePKIPath -}} + {{- if and .Values.global.secretsBackend.vault.enabled $vaultConnectCAEnabled }} + {{- with .Values.global.secretsBackend.vault }} + connect-ca-config.json: | + { + "connect": [ + { + "ca_config": [ + { + "address": "{{ .connectCA.address }}", + {{- if and .ca.secretName .ca.secretKey }} + "ca_file": "/consul/vault-ca/tls.crt", + {{- end }} + "intermediate_pki_path": "{{ .connectCA.intermediatePKIPath }}", + "root_pki_path": "{{ .connectCA.rootPKIPath }}", + "auth_method": { + "type": "kubernetes", + "params": { + "role": "{{ .consulServerRole }}" + } + } + } + ], + "ca_provider": "vault" + } + ] + } + {{- if .connectCA.additionalConfig }} + additional-connect-ca-config.json: | +{{ tpl .connectCA.additionalConfig $ | trimAll "\"" | indent 4 }} + {{- end }} + {{- end }} + {{- end }} extra-from-values.json: |- {{ tpl .Values.server.extraConfig . | trimAll "\"" | indent 4 }} {{- if .Values.global.acls.manageSystemACLs }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 2380486793..6803d2e760 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -1,10 +1,18 @@ {{- 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 }} {{- if and .Values.global.federation.enabled (not .Values.meshGateway.enabled) }}{{ fail "If global.federation.enabled is true, meshGateway.enabled must be true because mesh gateways are required for federation" }}{{ end }} {{- if and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert must also be provided" }}{{ end }} {{- if .Values.server.disableFsGroupSecurityContext }}{{ fail "server.disableFsGroupSecurityContext has been removed. Please use global.openshift.enabled instead." }}{{ end }} {{- if .Values.server.bootstrapExpect }}{{ if lt (int .Values.server.bootstrapExpect) (int .Values.server.replicas) }}{{ fail "server.bootstrapExpect cannot be less than server.replicas" }}{{ end }}{{ end }} {{- if (and (and .Values.global.tls.enabled .Values.global.tls.httpsOnly) (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics))}}{{ fail "global.metrics.enableAgentMetrics cannot be enabled if TLS (HTTPS only) is enabled" }}{{ end -}} +{{- if (and .Values.global.gossipEncryption.secretName (not .Values.global.gossipEncryption.secretKey)) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.gossipEncryption.secretName) .Values.global.gossipEncryption.secretKey) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.secretsBackend.vault.consulServerRole)) }}{{ fail "global.secretsBackend.vault.consulServerRole must be provided if global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName)) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert.secretName must also be provided" }}{{ end }} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.caCert.secretName)) }}{{ fail "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.enableAutoEncrypt)) }}{{ fail "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.secretsBackend.vault.consulCARole)) }}{{ fail "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} # StatefulSet to run the actual Consul server cluster. apiVersion: apps/v1 kind: StatefulSet @@ -46,6 +54,28 @@ spec: {{- toYaml .Values.server.extraLabels | nindent 8 }} {{- end }} annotations: + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": "{{ .Values.global.secretsBackend.vault.consulServerRole }}" + {{- 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.gossipEncryption.secretName }} + {{- with .Values.global.gossipEncryption }} + "vault.hashicorp.com/agent-inject-secret-gossip.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-gossip.txt": {{ template "consul.vaultGossipTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-servercert.crt": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.crt": {{ include "consul.serverTLSCertTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-servercert.key": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.key": {{ include "consul.serverTLSKeyTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ include "consul.serverTLSCATemplate" . }} + {{- end }} + {{- end }} "consul.hashicorp.com/connect-inject": "false" "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/server-config-configmap.yaml") . | sha256sum }} {{- if .Values.server.annotations }} @@ -83,7 +113,7 @@ spec: - name: config configMap: name: {{ template "consul.fullname" . }}-server-config - {{- if .Values.global.tls.enabled }} + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -107,6 +137,14 @@ spec: secret: secretName: {{ .Values.global.enterpriseLicense.secretName }} {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + secret: + secretName: {{ .Values.global.secretsBackend.vault.ca.secretName }} + items: + - key: {{ .Values.global.secretsBackend.vault.ca.secretKey }} + path: tls.crt + {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -157,6 +195,7 @@ spec: - name: CONSUL_DISABLE_PERM_MGMT value: "true" {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} - name: GOSSIP_KEY valueFrom: secretKeyRef: @@ -168,6 +207,7 @@ spec: key: {{ .Values.global.gossipEncryption.secretKey }} {{- end }} {{- end }} + {{- end }} {{- if .Values.global.tls.enabled }} - name: CONSUL_HTTP_ADDR value: https://localhost:8501 @@ -192,6 +232,10 @@ spec: - | CONSUL_FULLNAME="{{template "consul.fullname" . }}" + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} + GOSSIP_KEY=`cat /vault/secrets/gossip.txt` + {{- end }} + {{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }} {{ template "consul.recursors" }} {{- end }} @@ -203,9 +247,15 @@ spec: -bind=0.0.0.0 \ -bootstrap-expect={{ if .Values.server.bootstrapExpect }}{{ .Values.server.bootstrapExpect }}{{ else }}{{ .Values.server.replicas }}{{ end }} \ {{- if .Values.global.tls.enabled }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -hcl='ca_file = "/vault/secrets/serverca.crt"' \ + -hcl='cert_file = "/vault/secrets/servercert.crt"' \ + -hcl='key_file = "/vault/secrets/servercert.key"' \ + {{- else }} -hcl='ca_file = "/consul/tls/ca/tls.crt"' \ -hcl='cert_file = "/consul/tls/server/tls.crt"' \ -hcl='key_file = "/consul/tls/server/tls.key"' \ + {{- end }} {{- if .Values.global.tls.enableAutoEncrypt }} -hcl='auto_encrypt = {allow_tls = true}' \ {{- end }} @@ -268,7 +318,7 @@ spec: mountPath: /consul/data - name: config mountPath: /consul/config - {{- if .Values.global.tls.enabled }} + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} - name: consul-ca-cert mountPath: /consul/tls/ca/ readOnly: true @@ -286,6 +336,11 @@ spec: readOnly: true mountPath: /consul/userconfig/{{ .name }} {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + mountPath: /consul/vault-ca/ + readOnly: true + {{- end }} ports: {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} - name: http @@ -339,8 +394,7 @@ spec: - "-ec" - | {{- if .Values.global.tls.enabled }} - curl \ - --cacert /consul/tls/ca/tls.crt \ + curl -k \ https://127.0.0.1:8501/v1/status/leader \ {{- else }} curl http://127.0.0.1:8500/v1/status/leader \ diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index e1c0519e86..863eab2b10 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -33,6 +33,17 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- 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 }} + {{- end }} spec: serviceAccountName: {{ template "consul.fullname" . }}-sync-catalog {{- if .Values.global.tls.enabled }} diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index a53743918d..f99d9f1c26 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -53,6 +53,17 @@ spec: component: terminating-gateway terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} annotations: + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- end }} "consul.hashicorp.com/connect-inject": "false" {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} "prometheus.io/scrape": "true" diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index d3b1096921..9a8898cc10 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -1,5 +1,6 @@ {{- 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 }} # tls-init-cleanup job deletes Kubernetes secrets created by tls-init apiVersion: batch/v1 kind: Job @@ -63,3 +64,4 @@ spec: cpu: "50m" {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml index 4e2b24678b..ed99d5f297 100644 --- a/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-cleanup-podsecuritypolicy.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: @@ -39,3 +40,4 @@ spec: readOnlyRootFilesystem: false {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-cleanup-role.yaml b/charts/consul/templates/tls-init-cleanup-role.yaml index 77bd4bff61..aa66e3edc4 100644 --- a/charts/consul/templates/tls-init-cleanup-role.yaml +++ b/charts/consul/templates/tls-init-cleanup-role.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -37,3 +38,4 @@ rules: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml index 12cd08c74b..0d3bfe38e7 100644 --- a/charts/consul/templates/tls-init-cleanup-rolebinding.yaml +++ b/charts/consul/templates/tls-init-cleanup-rolebinding.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -23,3 +24,4 @@ subjects: name: {{ template "consul.fullname" . }}-tls-init-cleanup {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml index 198dab86f8..57e40dd3af 100644 --- a/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-cleanup-serviceaccount.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: v1 kind: ServiceAccount metadata: @@ -22,3 +23,4 @@ imagePullSecrets: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index 5cfefe1294..ba75d94460 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -1,5 +1,6 @@ {{- 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 }} # tls-init job generate Consul cluster CA and certificates for the Consul servers # and creates Kubernetes secrets for them. apiVersion: batch/v1 @@ -103,3 +104,4 @@ spec: cpu: "50m" {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-podsecuritypolicy.yaml b/charts/consul/templates/tls-init-podsecuritypolicy.yaml index 93aeb9f193..5d2a393955 100644 --- a/charts/consul/templates/tls-init-podsecuritypolicy.yaml +++ b/charts/consul/templates/tls-init-podsecuritypolicy.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: @@ -39,3 +40,4 @@ spec: readOnlyRootFilesystem: false {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-role.yaml b/charts/consul/templates/tls-init-role.yaml index a862925901..216602ee9f 100644 --- a/charts/consul/templates/tls-init-role.yaml +++ b/charts/consul/templates/tls-init-role.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -34,3 +35,4 @@ rules: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-rolebinding.yaml b/charts/consul/templates/tls-init-rolebinding.yaml index 1867826236..9b68d97d8c 100644 --- a/charts/consul/templates/tls-init-rolebinding.yaml +++ b/charts/consul/templates/tls-init-rolebinding.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: @@ -23,3 +24,4 @@ subjects: name: {{ template "consul.fullname" . }}-tls-init {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/tls-init-serviceaccount.yaml b/charts/consul/templates/tls-init-serviceaccount.yaml index 7d21ee811c..f8504da94c 100644 --- a/charts/consul/templates/tls-init-serviceaccount.yaml +++ b/charts/consul/templates/tls-init-serviceaccount.yaml @@ -1,5 +1,6 @@ {{- 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 }} apiVersion: v1 kind: ServiceAccount metadata: @@ -22,3 +23,4 @@ imagePullSecrets: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/consul/templates/ui-ingress.yaml b/charts/consul/templates/ui-ingress.yaml index 28d3069768..7b6e6bab4f 100644 --- a/charts/consul/templates/ui-ingress.yaml +++ b/charts/consul/templates/ui-ingress.yaml @@ -25,6 +25,9 @@ metadata: {{ tpl .Values.ui.ingress.annotations . | nindent 4 | trim }} {{- end }} spec: + {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "18" ) }} + ingressClassName: {{ .Values.ui.ingress.ingressClassName }} + {{- end }} rules: {{ $global := .Values.global }} {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 20b5aac856..c7fbf11c9c 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -602,7 +602,7 @@ load _helpers local actual=$(helm template \ -s templates/client-daemonset.yaml \ . | tee /dev/stderr | - yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | length > 0' | tee /dev/stderr) + yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY")' | tee /dev/stderr) [ "${actual}" = "" ] } @@ -1465,6 +1465,18 @@ rollingUpdate: [[ "$output" =~ "global.adminPartitions.name has to be \"default\" in the server cluster" ]] } +@test "client/DaemonSet: federation and admin partitions cannot be enabled together" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.federation.enabled=true' \ + . + + [ "$status" -eq 1 ] + [[ "$output" =~ "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" ]] +} + #-------------------------------------------------------------------- # extraContainers @@ -1540,3 +1552,286 @@ rollingUpdate: [ "${object}" = 1 ] } + +#-------------------------------------------------------------------- +# vault integration + +@test "client/DaemonSet: fail when vault is enabled but the consulClientRole is not provided" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.secretsBackend.vault.consulClientRole must be provided if global.secretsBackend.vault.enabled=true" ]] +} + +@test "client/DaemonSet: fail when vault, tls are enabled but no caCert provided" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.tls.enabled=true' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." ]] +} + +@test "client/DaemonSet: fail when vault, tls are enabled with a serverCert but no autoencrypt" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --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.tls.enabled=true' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "client/DaemonSet: fail when vault is enabled with tls but autoencrypt is disabled" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.server.serverCert.secretName=test' \ + --set 'global.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "client/DaemonSet: fail when vault is enabled with tls but no consulCARole is provided" { + cd `chart_dir` + run helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.server.serverCert.secretName=test' \ + --set 'global.tls.caCert.secretName=test' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.enabled=true' . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "client/DaemonSet: vault annotations not set by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/role"] | length > 0 ' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "client/DaemonSet: vault annotations added when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --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.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + [ "${actual}" = "foo" ] +} + +@test "client/DaemonSet: vault gossip annotations are set when gossip encryption enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --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.gossipEncryption.secretName=path/to/secret' \ + --set 'global.gossipEncryption.secretKey=gossip' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-gossip.txt"]' | tee /dev/stderr) + [ "${actual}" = "path/to/secret" ] + local actual="$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-template-gossip.txt"]' | tee /dev/stderr)" + local expected=$'{{- with secret \"path/to/secret\" -}}\n{{- .Data.data.gossip -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] +} + +@test "client/DaemonSet: GOSSIP_KEY env variable is not set and command defines GOSSIP_KEY when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --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.gossipEncryption.secretName=a/b/c/d' \ + --set 'global.gossipEncryption.secretKey=gossip' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec' | tee /dev/stderr) + + + local actual=$(echo $object | + yq -r '.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY")' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[] | select(.name=="consul") | .command | any(contains("GOSSIP_KEY="))' \ + | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "client/DaemonSet: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=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 "client/DaemonSet: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=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 "client/DaemonSet: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=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 "client/DaemonSet: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=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 "client/DaemonSet: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --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.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 $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/cert/ca" ] + + local actual="$(echo $object | + 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}" ] +} + +@test "client/DaemonSet: tls related volumes not attached and command is modified correctly when tls is enabled with vault" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-daemonset.yaml \ + --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.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=pki_int/ca/pem' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec' | tee /dev/stderr) + + + local actual=$(echo $object | + yq -r '.volumes[] | select(.name == "consul-ca-cert") | length > 0' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.volumes[] | select(.name == "consul-ca-key") | length > 0' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[0].volumeMounts[] | select(.name == "consul-client-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[0].volumeMounts[] | select(.name == "consul-ca-key")' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[0].command | any(contains("ca_file = \"/vault/secrets/serverca.crt\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/client-snapshot-agent-deployment.bats b/charts/consul/test/unit/client-snapshot-agent-deployment.bats index 48023c2bf7..50781a8f23 100644 --- a/charts/consul/test/unit/client-snapshot-agent-deployment.bats +++ b/charts/consul/test/unit/client-snapshot-agent-deployment.bats @@ -458,3 +458,127 @@ exec /bin/consul snapshot agent \' yq -r -c '.spec.template.spec.containers[0].env[] | select(.name == "CONSUL_LICENSE_PATH")' | tee /dev/stderr) [ "${actual}" = "" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "client/SnapshotAgentDeployment: configures server CA to come from vault when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-snapshot-agent-deployment.yaml \ + --set 'client.snapshotAgent.enabled=true' \ + --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=carole' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check annotations + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-init-first"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + [ "${actual}" = "carole" ] + local actual + 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) + [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] +} + +@test "client/SnapshotAgentDeployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-snapshot-agent-deployment.yaml \ + --set 'client.snapshotAgent.enabled=true' \ + --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=carole' \ + . | 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 "client/SnapshotAgentDeployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-snapshot-agent-deployment.yaml \ + --set 'client.snapshotAgent.enabled=true' \ + --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=carole' \ + --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 "client/SnapshotAgentDeployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-snapshot-agent-deployment.yaml \ + --set 'client.snapshotAgent.enabled=true' \ + --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=carole' \ + --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 "client/SnapshotAgentDeployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/client-snapshot-agent-deployment.yaml \ + --set 'client.snapshotAgent.enabled=true' \ + --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=carole' \ + --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" ] +} diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 1fb0dad743..e5ed980c1f 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -1549,6 +1549,134 @@ EOF } #-------------------------------------------------------------------- +# Vault + +@test "connectInject/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --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 "connectInject/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --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 "connectInject/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --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 "connectInject/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --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 "connectInject/Deployment: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --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" ] +} + # consulDestinationNamespace reserved name @test "connectInject/Deployment: fails when consulDestinationNamespace=system" { diff --git a/charts/consul/test/unit/controller-deployment.bats b/charts/consul/test/unit/controller-deployment.bats index 158c6edaaa..29cd9a1026 100644 --- a/charts/consul/test/unit/controller-deployment.bats +++ b/charts/consul/test/unit/controller-deployment.bats @@ -550,3 +550,133 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# Vault + +@test "controller/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/controller-deployment.yaml \ + --set 'controller.enabled=true' \ + --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 "controller/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/controller-deployment.yaml \ + --set 'controller.enabled=true' \ + --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 "controller/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/controller-deployment.yaml \ + --set 'controller.enabled=true' \ + --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 "controller/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/controller-deployment.yaml \ + --set 'controller.enabled=true' \ + --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 "controller/Deployment: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/controller-deployment.yaml \ + --set 'controller.enabled=true' \ + --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" ] +} + + diff --git a/charts/consul/test/unit/crd-partitionexports.bats b/charts/consul/test/unit/crd-exportedservices.bats similarity index 73% rename from charts/consul/test/unit/crd-partitionexports.bats rename to charts/consul/test/unit/crd-exportedservices.bats index 35463eea29..cf1a35a587 100644 --- a/charts/consul/test/unit/crd-partitionexports.bats +++ b/charts/consul/test/unit/crd-exportedservices.bats @@ -2,17 +2,17 @@ load _helpers -@test "partitionExports/CustomerResourceDefinition: disabled by default" { +@test "exportedServices/CustomerResourceDefinition: disabled by default" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-partitionexports.yaml \ + -s templates/crd-exportedservices.yaml \ . } -@test "partitionExports/CustomerResourceDefinition: enabled with controller.enabled=true" { +@test "exportedServices/CustomerResourceDefinition: enabled with controller.enabled=true" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-partitionexports.yaml \ + -s templates/crd-exportedservices.yaml \ --set 'controller.enabled=true' \ . | tee /dev/stderr | # The generated CRDs have "---" at the top which results in two objects diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats index b78b9c231d..4b5938ab91 100644 --- a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats @@ -39,24 +39,14 @@ load _helpers [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] } -@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretName is set" { +@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretName+key are set" { cd `chart_dir` run helm template \ -s templates/gossip-encryption-autogenerate-job.yaml \ --set 'global.gossipEncryption.autoGenerate=true' \ --set 'global.gossipEncryption.secretName=name' \ - . - [ "$status" -eq 1 ] - [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] -} - -@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretKey is set" { - cd `chart_dir` - run helm template \ - -s templates/gossip-encryption-autogenerate-job.yaml \ - --set 'global.gossipEncryption.autoGenerate=true' \ - --set 'global.gossipEncryption.secretKey=key' \ - . + --set 'global.gossipEncryption.secretKey=name' \ + . [ "$status" -eq 1 ] [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] } diff --git a/charts/consul/test/unit/helpers.bats b/charts/consul/test/unit/helpers.bats index 8e02152ff2..63148e3424 100644 --- a/charts/consul/test/unit/helpers.bats +++ b/charts/consul/test/unit/helpers.bats @@ -284,3 +284,26 @@ load _helpers [ "${actual}" = "" ] } + +@test "helper/consul.getAutoEncryptClientCA: uses the correct -ca-file when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/tests/test-runner.yaml \ + --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.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + --set 'global.tls.caCert.secretName=pki_int/ca/pem' \ + --set 'server.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.initContainers[] | select(.name == "get-auto-encrypt-client-ca")' | tee /dev/stderr) + + actual=$(echo $object | jq '.command | join(" ") | contains("-ca-file=/vault/secrets/serverca.crt")') + [ "${actual}" = "true" ] + + actual=$(echo $object | jq '.volumeMounts[] | select(.name == "consul-ca-cert")') + [ "${actual}" = "" ] +} diff --git a/charts/consul/test/unit/ingress-gateways-deployment.bats b/charts/consul/test/unit/ingress-gateways-deployment.bats index c4b623e4be..ee1ac5f303 100644 --- a/charts/consul/test/unit/ingress-gateways-deployment.bats +++ b/charts/consul/test/unit/ingress-gateways-deployment.bats @@ -1485,3 +1485,128 @@ EOF local actual=$(echo $object | yq '.[2] | length > 0' | tee /dev/stderr) [ "${actual}" = "false" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "ingressGateway/Deployment: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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=carole' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check annotations + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-init-first"]' | 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}" = "carole" ] + + local 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) + [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] +} + +@test "ingressGateway/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "ingressGateway/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "ingressGateway/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "ingressGateway/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index fe5b09e615..cc878e81c8 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -593,6 +593,139 @@ load _helpers [ "${actual}" = "key" ] } +@test "serverACLInit/Job: configures server CA to come from vault when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.server.serverCert.secretName=foo' \ + --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=carole' \ + . | tee /dev/stderr | + 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) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + [ "${actual}" = "carole" ] + local actual + 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) + [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] + + # Check that the consul-ca-cert volume is not attached + local actual=$(echo $object | jq -r '.spec.volumes') + [ "${actual}" = "null" ] + + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name="post-install-job").volumeMounts') + [ "${actual}" = "null" ] +} + +@test "serverACLInit/Job: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.server.serverCert.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=carole' \ + . | 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 "serverACLInit/Job: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.server.serverCert.secretName=foo' \ + --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=carole' \ + --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 "serverACLInit/Job: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.server.serverCert.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=carole' \ + --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 "serverACLInit/Job: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.server.serverCert.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=carole' \ + --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" ] +} + #-------------------------------------------------------------------- # namespaces diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index f7fd6ce033..cea3ef7eff 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -201,3 +201,298 @@ load _helpers yq -r '.data["acl-config.json"]' | yq -r '.acl.enable_token_replication' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault Connect CA + +@test "server/ConfigMap: doesn't add connect CA config by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled but vault address, root and int PKI paths are not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and vault address is set, but root and int PKI paths are not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and root pki path is set, but vault address and int PKI paths are not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and int path is set, but vault address and root PKI paths are not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and root and int paths are set, but vault address is not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and root path and address are set, but int path is not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't add connect CA config when vault is enabled and int path and address are set, but root path is not set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.intPKIPath=int' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.intPKIPath=int' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: adds connect CA config when vault is enabled and connect CA are configured" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"]' | tee /dev/stderr) + [ "${actual}" = '"{\n \"connect\": [\n {\n \"ca_config\": [\n {\n \"address\": \"example.com\",\n \"intermediate_pki_path\": \"int\",\n \"root_pki_path\": \"root\",\n \"auth_method\": {\n \"type\": \"kubernetes\",\n \"params\": {\n \"role\": \"foo\"\n }\n }\n }\n ],\n \"ca_provider\": \"vault\"\n }\n ]\n}\n"' ] + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"]' | tee /dev/stderr) + [ "${actual}" = '"{}\n"' ] +} + +@test "server/ConfigMap: can set additional connect CA config" { + cd `chart_dir` + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + --set 'global.secretsBackend.vault.connectCA.additionalConfig="{\"hello\": \"world\"}"' \ + . | tee /dev/stderr | + yq '.data["additional-connect-ca-config.json"]' | tee /dev/stderr) + [ "${actual}" = '"{\"hello\": \"world\"}\n"' ] +} + +@test "server/ConfigMap: doesn't set Vault CA cert in connect CA config by default" { + cd `chart_dir` + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | contains("\"ca_file\": \"/consul/vault-ca/tls.crt\"")' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't set Vault CA cert in connect CA config when vault CA secret name is set but secret key is not" { + cd `chart_dir` + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | contains("\"ca_file\": \"/consul/vault-ca/tls.crt\"")' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't set Vault CA cert in connect CA config when vault CA secret key is set but secret name is not" { + cd `chart_dir` + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | contains("\"ca_file\": \"/consul/vault-ca/tls.crt\"")' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/ConfigMap: doesn't set Vault CA cert in connect CA config when both vault CA secret name and key are set" { + cd `chart_dir` + + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.connectCA.address=example.com' \ + --set 'global.secretsBackend.vault.connectCA.rootPKIPath=root' \ + --set 'global.secretsBackend.vault.connectCA.intermediatePKIPath=int' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq '.data["connect-ca-config.json"] | contains("\"ca_file\": \"/consul/vault-ca/tls.crt\"")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index e11018f279..15c03711db 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -52,6 +52,21 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# admin-partitions + +@test "server/StatefulSet: federation and admin partitions cannot be enabled together" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.federation.enabled=true' \ + . + + [ "$status" -eq 1 ] + [[ "$output" =~ "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" ]] +} + #-------------------------------------------------------------------- # image @@ -862,7 +877,7 @@ load _helpers #-------------------------------------------------------------------- # global.openshift.enabled & client.containerSecurityContext -@test "client/DaemonSet: container level securityContexts are not set when global.openshift.enabled=true" { +@test "server/StatefulSet: container level securityContexts are not set when global.openshift.enabled=true" { cd `chart_dir` local manifest=$(helm template \ -s templates/server-statefulset.yaml \ @@ -882,7 +897,7 @@ load _helpers local actual=$(helm template \ -s templates/server-statefulset.yaml \ . | tee /dev/stderr | - yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | length > 0' | tee /dev/stderr) + yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY")' | tee /dev/stderr) [ "${actual}" = "" ] } @@ -907,25 +922,20 @@ load _helpers [ "${actual}" = "true" ] } - -@test "server/StatefulSet: gossip encryption disabled in server StatefulSet when secretName is missing" { +@test "server/StatefulSet: fail if global.gossipEncyption.gossipEncryption.secretName is set but not global.gossipEncyption.secretKey" { cd `chart_dir` - local actual=$(helm template \ + run helm template \ -s templates/server-statefulset.yaml \ - --set 'global.gossipEncryption.secretKey=bar' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | length > 0' | tee /dev/stderr) - [ "${actual}" = "" ] + --set 'global.gossipEncryption.secretName=bar' . + [[ "$output" =~ "gossipEncryption.secretKey and secretName must both be specified." ]] } -@test "server/StatefulSet: gossip encryption disabled in server StatefulSet when secretKey is missing" { +@test "server/StatefulSet: fail if global.gossipEncyption.gossipEncryption.secretKey is set but not global.gossipEncyption.secretName" { cd `chart_dir` - local actual=$(helm template \ + run helm template \ -s templates/server-statefulset.yaml \ - --set 'global.gossipEncryption.secretName=foo' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | length > 0' | tee /dev/stderr) - [ "${actual}" = "" ] + --set 'global.gossipEncryption.secretKey=bar' . + [[ "$output" =~ "gossipEncryption.secretKey and secretName must both be specified." ]] } @test "server/StatefulSet: gossip environment variable present in server StatefulSet when all config is provided" { @@ -1085,16 +1095,6 @@ load _helpers [ "${actual}" = "true" ] } -@test "server/StatefulSet: CA certificate is specified when TLS is enabled" { - cd `chart_dir` - local actual=$(helm template \ - -s templates/server-statefulset.yaml \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.containers[0].readinessProbe.exec.command | join(" ") | contains("--cacert /consul/tls/ca/tls.crt")' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - @test "server/StatefulSet: HTTP is disabled in agent when httpsOnly is enabled" { cd `chart_dir` local actual=$(helm template \ @@ -1459,3 +1459,338 @@ load _helpers [ "${object}" = 1 ] } + +#-------------------------------------------------------------------- +# vault integration + +@test "server/StatefulSet: fail when vault is enabled but the consulServerRole is not provided" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.secretsBackend.vault.consulServerRole must be provided if global.secretsBackend.vault.enabled=true" ]] +} + +@test "server/StatefulSet: fail when vault is enabled with tls but autoencrypt is disabled" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.server.serverCert.secretName=test' \ + --set 'global.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "server/StatefulSet: fail when vault, tls are enabled but no caCert provided" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.tls.enabled=true' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." ]] +} + +@test "server/StatefulSet: fail when vault, tls are enabled with a serverCert but no autoencrypt" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.tls.enabled=true' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "server/StatefulSet: fail when vault is enabled with tls but no consulCARole is provided" { + cd `chart_dir` + run helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.server.serverCert.secretName=test' \ + --set 'global.tls.caCert.secretName=test' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.enabled=true' . + [ "$status" -eq 1 ] + [[ "$output" =~ "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" ]] +} + +@test "server/StatefulSet: vault annotations not set by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/role"] | length > 0 ' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "server/StatefulSet: vault annotations added when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject"] | length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + [ "${actual}" = "test" ] +} + +@test "server/StatefulSet: vault gossip annotations are correct when enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.gossipEncryption.secretName=path/to/secret' \ + --set 'global.gossipEncryption.secretKey=gossip' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-gossip.txt"]' | tee /dev/stderr) + [ "${actual}" = "path/to/secret" ] + local actual=$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-template-gossip.txt"]' | tee /dev/stderr) + local actual="$(echo $object | + yq -r '.annotations["vault.hashicorp.com/agent-inject-template-gossip.txt"]' | tee /dev/stderr)" + local expected=$'{{- with secret \"path/to/secret\" -}}\n{{- .Data.data.gossip -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] +} + +@test "server/StatefulSet: vault no GOSSIP_KEY env variable and command defines GOSSIP_KEY" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.gossipEncryption.secretName=a/b/c/d' \ + --set 'global.gossipEncryption.secretKey=gossip' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY")' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[] | select(.name=="consul") | .command | any(contains("GOSSIP_KEY="))' \ + | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/StatefulSet: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check that the volume is defined. + local actual=$(echo $object | + yq -r '.spec.volumes[] | select(.name=="vault-ca")' | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that the volume mount is added. + local actual=$(echo $object | + yq -r '.spec.containers[] | select(.name=="consul").volumeMounts[] | select(.name=="vault-ca")' \ + | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that Vault agent annotations are added. + 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 "server/StatefulSet: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check that the volume is defined. + local actual=$(echo $object | + yq -r '.spec.volumes[] | select(.name=="vault-ca")' | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that the volume mount is added. + local actual=$(echo $object | + yq -r '.spec.containers[] | select(.name=="consul").volumeMounts[] | select(.name=="vault-ca")' \ + | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that Vault agent annotations are added. + 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 "server/StatefulSet: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check that the volume is defined. + local actual=$(echo $object | + yq -r '.spec.volumes[] | select(.name=="vault-ca")' | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that the volume mount is added. + local actual=$(echo $object | + yq -r '.spec.containers[] | select(.name=="consul").volumeMounts[] | select(.name=="vault-ca")' \ + | tee /dev/stderr) + [ "${actual}" = "" ] + + # Check that Vault agent annotations are added. + 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 "server/StatefulSet: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=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) + + # Check that the volume is defined. + local actual=$(echo $object | + yq -r '.spec.volumes[] | select(.name=="vault-ca").secret.secretName' | tee /dev/stderr) + [ "${actual}" = "ca" ] + + # Check that the volume mount is added. + local actual=$(echo $object | + yq -r '.spec.containers[] | select(.name=="consul").volumeMounts[] | select(.name=="vault-ca")' \ + | tee /dev/stderr) + [ "${actual}" != "" ] + + # Check that Vault agent annotations are added. + 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 "server/StatefulSet: vault tls annotations are set when tls is enabled and command modified correctly" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc2' \ + --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.tls.caCert.secretName=pki_int/cert/ca' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual="$(echo $object | + yq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/cert/ca" ] + + local actual="$(echo $object | + yq -r '.metadata.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 $object | + yq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-servercert.crt"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/issue/test" ] + + local actual=$(echo $object | + yq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-servercert.crt"]' | tee /dev/stderr) + local expected=$'{{- with secret \"pki_int/issue/test\" \"common_name=server.dc2.consul\"\n\"ttl=1h\" \"alt_names=localhost,RELEASE-NAME-consul-server,*.RELEASE-NAME-consul-server,*.RELEASE-NAME-consul-server.default,*.RELEASE-NAME-consul-server.default.svc,*.server.dc2.consul\" \"ip_sans=127.0.0.1\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] + + local actual="$(echo $object | + yq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-servercert.key"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/issue/test" ] + + local actual="$(echo $object | + yq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-servercert.key"]' | tee /dev/stderr)" + local expected=$'{{- with secret \"pki_int/issue/test\" \"common_name=server.dc2.consul\"\n\"ttl=1h\" \"alt_names=localhost,RELEASE-NAME-consul-server,*.RELEASE-NAME-consul-server,*.RELEASE-NAME-consul-server.default,*.RELEASE-NAME-consul-server.default.svc,*.server.dc2.consul\" \"ip_sans=127.0.0.1\" -}}\n{{- .Data.private_key -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] + + local actual=$(echo $object | + yq -r '.spec.containers[0].command | any(contains("ca_file = \"/vault/secrets/serverca.crt\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/StatefulSet: tls related volumes not attached when tls is enabled on vault" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --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.spec' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.volumes[] | select(.name == "consul-ca-cert") | length > 0' | tee /dev/stderr) + [ "${actual}" = "" ] + + local actual=$(echo $object | + yq -r '.containers[0].volumeMounts[] | select(.name == "consul-ca-key")' | tee /dev/stderr) + [ "${actual}" = "" ] +} diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index 2b297d3cd7..aac942894d 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -979,6 +979,129 @@ load _helpers } #-------------------------------------------------------------------- +# Vault + +@test "syncCatalog/Deployment: configures server CA to come from vault when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --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=carole' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check annotations + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-init-first"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + [ "${actual}" = "carole" ] + local actual + 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) + [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] +} + +@test "syncCatalog/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --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=carole' \ + . | 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 "syncCatalog/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --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=carole' \ + --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 "syncCatalog/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --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=carole' \ + --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 "syncCatalog/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --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=carole' \ + --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" ] +} + # consulDestinationNamespace reserved name @test "syncCatalog/Deployment: fails when consulDestinationNamespace=system" { diff --git a/charts/consul/test/unit/terminating-gateways-deployment.bats b/charts/consul/test/unit/terminating-gateways-deployment.bats index 5a36e639be..e0bbdcd0a4 100644 --- a/charts/consul/test/unit/terminating-gateways-deployment.bats +++ b/charts/consul/test/unit/terminating-gateways-deployment.bats @@ -1295,3 +1295,127 @@ EOF local actual=$(echo $object | yq '.[2] | length > 0' | tee /dev/stderr) [ "${actual}" = "false" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "terminatingGateway/Deployment: configures server CA to come from vault when vault is enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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=carole' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + # Check annotations + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-init-first"]' | 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}" = "carole" ] + + local 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) + [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] +} + +@test "terminatingGateway/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "terminatingGateway/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "terminatingGateway/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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 "terminatingGateway/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + --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" ] +} diff --git a/charts/consul/test/unit/tls-init-cleanup-job.bats b/charts/consul/test/unit/tls-init-cleanup-job.bats index d75abf7d6d..76da65bfe5 100644 --- a/charts/consul/test/unit/tls-init-cleanup-job.bats +++ b/charts/consul/test/unit/tls-init-cleanup-job.bats @@ -58,3 +58,20 @@ load _helpers yq 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInitCleanup/Job: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-cleanup-podsecuritypolicy.bats b/charts/consul/test/unit/tls-init-cleanup-podsecuritypolicy.bats index 34d747aecb..72d1812c69 100644 --- a/charts/consul/test/unit/tls-init-cleanup-podsecuritypolicy.bats +++ b/charts/consul/test/unit/tls-init-cleanup-podsecuritypolicy.bats @@ -47,3 +47,20 @@ load _helpers yq -s 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInitCleanup/PodSecurityPolicy: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-cleanup-podsecuritypolicy.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-cleanup-role.bats b/charts/consul/test/unit/tls-init-cleanup-role.bats index ad3ca7bbe6..cfcf5be7ba 100644 --- a/charts/consul/test/unit/tls-init-cleanup-role.bats +++ b/charts/consul/test/unit/tls-init-cleanup-role.bats @@ -70,3 +70,20 @@ load _helpers [ "${actual}" = "RELEASE-NAME-consul-tls-init-cleanup" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInitCleanup/Role: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-cleanup-role.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-cleanup-rolebinding.bats b/charts/consul/test/unit/tls-init-cleanup-rolebinding.bats index 5fd3632ed4..07cd485852 100644 --- a/charts/consul/test/unit/tls-init-cleanup-rolebinding.bats +++ b/charts/consul/test/unit/tls-init-cleanup-rolebinding.bats @@ -58,3 +58,20 @@ load _helpers yq 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInitCleanup/RoleBinding: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-cleanup-rolebinding.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-cleanup-serviceaccount.bats b/charts/consul/test/unit/tls-init-cleanup-serviceaccount.bats index d2a89e4e48..283c1ad73f 100644 --- a/charts/consul/test/unit/tls-init-cleanup-serviceaccount.bats +++ b/charts/consul/test/unit/tls-init-cleanup-serviceaccount.bats @@ -79,3 +79,20 @@ load _helpers yq -r '.imagePullSecrets[1].name' | tee /dev/stderr) [ "${actual}" = "my-secret2" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInitCleanup/ServiceAccount: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-cleanup-serviceaccount.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-job.bats b/charts/consul/test/unit/tls-init-job.bats index 9c751fa3d8..8db52fcfbd 100644 --- a/charts/consul/test/unit/tls-init-job.bats +++ b/charts/consul/test/unit/tls-init-job.bats @@ -115,3 +115,20 @@ load _helpers actual=$(echo $spec | jq -r '.containers[0].command | join(" ") | contains("consul tls ca create")' | tee /dev/stderr) [ "${actual}" = "false" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInit/Job: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-podsecuritypolicy.bats b/charts/consul/test/unit/tls-init-podsecuritypolicy.bats index 5ec0729d05..459097a9db 100644 --- a/charts/consul/test/unit/tls-init-podsecuritypolicy.bats +++ b/charts/consul/test/unit/tls-init-podsecuritypolicy.bats @@ -47,3 +47,20 @@ load _helpers yq -s 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInit/PodSecurityPolicy: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-podsecuritypolicy.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-role.bats b/charts/consul/test/unit/tls-init-role.bats index fe601be911..12af5ed2d0 100644 --- a/charts/consul/test/unit/tls-init-role.bats +++ b/charts/consul/test/unit/tls-init-role.bats @@ -70,3 +70,20 @@ load _helpers [ "${actual}" = "RELEASE-NAME-consul-tls-init" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInit/Role: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-role.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-rolebinding.bats b/charts/consul/test/unit/tls-init-rolebinding.bats index cac2a97f53..3085d4b85b 100644 --- a/charts/consul/test/unit/tls-init-rolebinding.bats +++ b/charts/consul/test/unit/tls-init-rolebinding.bats @@ -58,3 +58,20 @@ load _helpers yq 'length > 0' | tee /dev/stderr) [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# Vault + +@test "tlsInit/RoleBinding: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-rolebinding.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/tls-init-serviceaccount.bats b/charts/consul/test/unit/tls-init-serviceaccount.bats index 9eb3560ba7..3acf6f281e 100644 --- a/charts/consul/test/unit/tls-init-serviceaccount.bats +++ b/charts/consul/test/unit/tls-init-serviceaccount.bats @@ -80,3 +80,19 @@ load _helpers [ "${actual}" = "my-secret2" ] } +#-------------------------------------------------------------------- +# Vault + +@test "tlsInit/ServiceAccount: disabled with global.secretsBackend.vault.enabled=true and global.tls.enabled=true" { + cd `chart_dir` + assert_empty helm template \ + -s templates/tls-init-serviceaccount.yaml \ + --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.tls.caCert.secretName=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + . +} diff --git a/charts/consul/test/unit/ui-ingress.bats b/charts/consul/test/unit/ui-ingress.bats index f26d181d2b..aaa4e56653 100755 --- a/charts/consul/test/unit/ui-ingress.bats +++ b/charts/consul/test/unit/ui-ingress.bats @@ -59,59 +59,73 @@ load _helpers [ "${actual}" = "foo.com" ] } -@test "ui/Ingress: exposes single port 80 when global.tls.enabled=false" { -# todo: test for Kube versions < 1.19 when helm supports --kube-version flag (https://github.com/helm/helm/pull/9040) -# local actual=$(helm template \ -# -s templates/ui-ingress.yaml \ -# --set 'ui.ingress.enabled=true' \ -# --set 'global.tls.enabled=false' \ -# --set 'ui.ingress.hosts[0].host=foo.com' \ -# --kube-version "1.18" \ -# . | tee /dev/stderr | -# yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) +@test "ui/Ingress: exposes single port 80 when global.tls.enabled=false when Kube version < 1.19" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'global.tls.enabled=false' \ + --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.18" \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) + [ "${actual}" = "80" ] +} + +@test "ui/Ingress: exposes single port 80 when global.tls.enabled=false when Kube version >= 1.19" { cd `chart_dir` local actual=$(helm template \ -s templates/ui-ingress.yaml \ --set 'ui.ingress.enabled=true' \ --set 'global.tls.enabled=false' \ --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.19" \ . | tee /dev/stderr | yq -r '.spec.rules[0].http.paths[0].backend.service.port.number' | tee /dev/stderr) [ "${actual}" = "80" ] } -@test "ui/Ingress: exposes single port 443 when global.tls.enabled=true and global.tls.httpsOnly=true" { -# todo: test for Kube versions < 1.19 when helm supports --kube-version flag (https://github.com/helm/helm/pull/9040) -# local actual=$(helm template \ -# -s templates/ui-ingress.yaml \ -# --set 'ui.ingress.enabled=true' \ -# --set 'global.tls.enabled=true' \ -# --set 'ui.ingress.hosts[0].host=foo.com' \ -# --kube-version "1.18" \ -# . | tee /dev/stderr | -# yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) +@test "ui/Ingress: exposes single port 443 when global.tls.enabled=true and global.tls.httpsOnly=true when Kube version < 1.19" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.18" \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) + [ "${actual}" = "443" ] +} + +@test "ui/Ingress: exposes single port 443 when global.tls.enabled=true and global.tls.httpsOnly=true when Kube version >= 1.19" { cd `chart_dir` local actual=$(helm template \ -s templates/ui-ingress.yaml \ --set 'ui.ingress.enabled=true' \ --set 'global.tls.enabled=true' \ --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.19" \ . | tee /dev/stderr | yq -r '.spec.rules[0].http.paths[0].backend.service.port.number' | tee /dev/stderr) [ "${actual}" = "443" ] } -@test "ui/Ingress: exposes the port 80 when global.tls.enabled=true and global.tls.httpsOnly=false" { -# todo: test for Kube versions < 1.19 when helm supports --kube-version flag (https://github.com/helm/helm/pull/9040) -# local actual=$(helm template \ -# -s templates/ui-ingress.yaml \ -# --set 'ui.ingress.enabled=true' \ -# --set 'global.tls.enabled=true' \ -# --set 'global.tls.httpsOnly=false' \ -# --set 'ui.ingress.hosts[0].host=foo.com' \ -# --kube-version "1.18" \ -# . | tee /dev/stderr | -# yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) +@test "ui/Ingress: exposes the port 80 when global.tls.enabled=true and global.tls.httpsOnly=false when Kube version < 1.19" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.httpsOnly=false' \ + --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.18" \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[0].backend.servicePort' | tee /dev/stderr) + [ "${actual}" = "80" ] +} + +@test "ui/Ingress: exposes the port 80 when global.tls.enabled=true and global.tls.httpsOnly=false when Kube version >= 1.19" { cd `chart_dir` local actual=$(helm template \ -s templates/ui-ingress.yaml \ @@ -119,22 +133,27 @@ load _helpers --set 'global.tls.enabled=true' \ --set 'global.tls.httpsOnly=false' \ --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.19" \ . | tee /dev/stderr | yq -r '.spec.rules[0].http.paths[0].backend.service.port.number' | tee /dev/stderr) [ "${actual}" = "80" ] } -@test "ui/Ingress: exposes the port 443 when global.tls.enabled=true and global.tls.httpsOnly=false" { -# todo: test for Kube versions < 1.19 when helm supports --kube-version flag (https://github.com/helm/helm/pull/9040) -# local actual=$(helm template \ -# -s templates/ui-ingress.yaml \ -# --set 'ui.ingress.enabled=true' \ -# --set 'global.tls.enabled=true' \ -# --set 'global.tls.httpsOnly=false' \ -# --set 'ui.ingress.hosts[0].host=foo.com' \ -# --kube-version "1.18" \ -# . | tee /dev/stderr | -# yq -r '.spec.rules[0].http.paths[1].backend.servicePort' | tee /dev/stderr) +@test "ui/Ingress: exposes the port 443 when global.tls.enabled=true and global.tls.httpsOnly=false when Kube version < 1.19" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.httpsOnly=false' \ + --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.18" \ + . | tee /dev/stderr | + yq -r '.spec.rules[0].http.paths[1].backend.servicePort' | tee /dev/stderr) + [ "${actual}" = "443" ] +} + +@test "ui/Ingress: exposes the port 443 when global.tls.enabled=true and global.tls.httpsOnly=false when Kube version >= 1.19" { cd `chart_dir` local actual=$(helm template \ -s templates/ui-ingress.yaml \ @@ -142,6 +161,7 @@ load _helpers --set 'global.tls.enabled=true' \ --set 'global.tls.httpsOnly=false' \ --set 'ui.ingress.hosts[0].host=foo.com' \ + --kube-version "1.19" \ . | tee /dev/stderr | yq -r '.spec.rules[0].http.paths[1].backend.service.port.number' | tee /dev/stderr) [ "${actual}" = "443" ] @@ -234,3 +254,40 @@ load _helpers yq -r '.spec.rules[0].http.paths[0].pathType' | tee /dev/stderr) [ "${actual}" = "ImplementationSpecific" ] } + +#-------------------------------------------------------------------- +# ingressClassName + +@test "ui/Ingress: no ingressClassName by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ingressClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "ui/Ingress: can set ingressClassName" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'ui.ingress.ingressClassName=nginx' \ + . | tee /dev/stderr | + yq -r '.spec.ingressClassName' | tee /dev/stderr) + [ "${actual}" = "nginx" ] +} + +@test "ui/Ingress: cannot set ingressClassName for Kube version < 1.18" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/ui-ingress.yaml \ + --set 'ui.ingress.enabled=true' \ + --set 'ui.ingress.ingressClassName=nginx' \ + --kube-version "1.17" \ + . | tee /dev/stderr | + yq -r '.spec.ingressClassName' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index a6ab7223fe..7cf9156fbf 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -105,7 +105,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:0.37.0" + imageK8S: "hashicorp/consul-k8s-control-plane:0.38.0" # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running @@ -117,25 +117,127 @@ global: # created by this chart. See https://kubernetes.io/docs/concepts/policy/pod-security-policy/. enablePodSecurityPolicies: false - # Configures Consul's gossip encryption key, set as a Kubernetes secret + # 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. + # + # 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. + # It can have Consul as its storage backend as long as that Consul cluster is not running on this Kubernetes cluster + # and is being managed separately from this Helm installation. + # + # Note: When using Vault KV2 secrets engines the "data" field is implicitly required for Vault API calls, + # secretName should be in the form of "vault-kv2-mount-path/data/secret-name". + # secretKey should be in the form of "key". + secretsBackend: + vault: + # Enabling the Vault secrets backend will replace Kubernetes secrets with referenced Vault secrets. + enabled: false + + # The Vault role for the Consul server. + # The role must be connected to the Consul server's service account and + # have a policy with read capabilities for the following secrets: + # - gossip encryption key defined by `global.gossipEncryption.secretName`. + # To discover the service account name of the Consul server, run + # ``` + # helm template -s templates/server-serviceaccount.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulServerRole: "" + + # The Vault role for the Consul client. + # The role must be connected to the Consul client's service account and + # have a policy with read capabilities for the following secrets: + # - gossip encryption key defined by `global.gossipEncryption.secretName`. + # To discover the service account name of the Consul server, run + # ``` + # helm template -s templates/client-daemonset.yaml charts/consul + # ``` + # and check the name of `metadata.name`. + consulClientRole: "" + + # The Vault role for all Consul components to read the Consul's server's CA Certificate (unauthenticated). + # The role should be connected to the service accounts of all Consul components, or alternatively `*` since it + # will be used only against the `pki/cert/ca` endpoint which is unauthenticated. A policy must be created which grants + # read capabilities to `global.tls.caCert.secretName`, which is usually `pki/cert/ca`. + consulCARole: "" + + # Configuration for Vault server CA certificate. This certificate will be mounted + # to any pod where Vault agent needs to run. + ca: + # secretName is the name of the Kubernetes secret that holds the Vault CA certificate. + # A Kubernetes secret must be in the same namespace that Consul is installed into. + secretName: "" + # secretKey is the key within the Kubernetes secret that holds the Vault CA certificate. + secretKey: "" + + # Configuration for the Vault Connect CA provider. + # 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 + # for information on how to configure the Vault policies. + connectCA: + # The address of the Vault server. + address: "" + + # The path to a PKI secrets engine for the root certificate. + # Please see https://www.consul.io/docs/connect/ca/vault#rootpkipath. + rootPKIPath: "" + + # The path to a PKI secrets engine for the generated intermediate certificate. + # Please see https://www.consul.io/docs/connect/ca/vault#intermediatepkipath. + intermediatePKIPath: "" + + # Additional Connect CA configuration in JSON format. + # Please see https://www.consul.io/docs/connect/ca/vault#common-ca-config-options + # for additional configuration options. + # + # Example: + # + # ```yaml + # additionalConfig: | + # { + # "connect": [{ + # "ca_config": [{ + # "leaf_cert_ttl": "36h" + # }] + # }] + # } + # ``` + additionalConfig: | + {} + + # Configures Consul's gossip encryption key. # (see `-encrypt` (https://consul.io/docs/agent/options#_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. # Values for secretName and secretKey should not be set if autoGenerate is true. # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate - # a Kubernetes secret referencing these values. + # 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]. # # ``` # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) # ``` + # + # Vault CLI Example: + # ``` + # $ vault kv put consul/secrets/gossip key=$(consul keygen) + # ``` + # `gossipEncryption.secretName="consul/data/secrets/gossip"` + # `gossipEncryption.secretKey="key"` + gossipEncryption: # Automatically generate a gossip encryption key and save it to a Kubernetes secret. autoGenerate: false - # secretName is the name of the Kubernetes secret that holds the gossip - # encryption key. The secret must be in the same namespace that Consul is installed into. + # secretName is the name of the Kubernetes secret or Vault secret path that holds the gossip + # encryption key. A Kubernetes secret must be in the same namespace that Consul is installed into. secretName: "" - # secretKey is the key within the Kubernetes secret that holds the gossip + # secretKey is the key within the Kubernetes secret or Vault secret key that holds the gossip # encryption key. secretKey: "" @@ -183,15 +285,19 @@ global: # both clients and servers and to only accept HTTPS connections. httpsOnly: true - # A Kubernetes secret containing the certificate of the CA to use for - # TLS communication within the Consul cluster. If you have generated the CA yourself - # with the consul CLI, you could use the following command to create the secret + # A secret containing the certificate of the CA to use for TLS communication within the Consul cluster. + # If you have generated the CA yourself with the consul CLI, you could use the following command to create the secret # in Kubernetes: # # ```bash # kubectl create secret generic consul-ca-cert \ # --from-file='tls.crt=./consul-agent-ca.pem' # ``` + # If you are using Vault as a secrets backend with TLS, `caCert.secretName` must be provided and should reference + # the CA path for your PKI secrets engine. This should be of the form `pki/cert/ca` where `pki` is the mount point of your PKI secrets engine. + # A read policy must be created and associated with the CA cert path for `global.tls.caCert.secretName`. + # This will be consumed by the `global.secretsBackend.vault.consulCARole` role by all Consul components. + # When using Vault the secretKey is not used. caCert: # The name of the Kubernetes secret. secretName: null @@ -379,10 +485,11 @@ server: # @type: int bootstrapExpect: null - # A Kubernetes secret containing a certificate & key for the server agents to use + # A secret containing a certificate & key for the server agents to use # for TLS communication within the Consul cluster. Cert needs to be provided with # additional DNS name SANs so that it will work within the Kubernetes cluster: # + # Kubernetes Secrets backend: # ```bash # consul tls cert create -server -days=730 -domain=consul -ca=consul-agent-ca.pem \ # -key=consul-agent-ca-key.pem -dc={{datacenter}} \ @@ -394,8 +501,7 @@ server: # -additional-dnsname="server.{{datacenter}}.{{domain}}" # ``` # - # If you have generated the - # server-cert yourself with the consul CLI, you could use the following command + # If you have generated the server-cert yourself with the consul CLI, you could use the following command # to create the secret in Kubernetes: # # ```bash @@ -403,8 +509,16 @@ server: # --from-file='tls.crt=./dc1-server-consul-0.pem' # --from-file='tls.key=./dc1-server-consul-0-key.pem' # ``` + # + # 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 + # 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: - # The name of the Kubernetes secret. + # The name of the Kubernetes secret or Vault secret path containing the PEM encoded server certificate. secretName: null # Exposes the servers' gossip and RPC ports as hostPorts. To enable a client @@ -442,6 +556,8 @@ 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. + # See https://www.consul.io/docs/install/performance#read-write-tuning for considerations around choosing a + # performant storage class. # @type: string storageClass: null @@ -1222,6 +1338,9 @@ ui: # @type: boolean enabled: false + # Optionally set the ingressClassName. + ingressClassName: "" + # pathType override - see: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types pathType: Prefix diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index 001c1d3c2a..7fc038a5e0 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -3,16 +3,18 @@ package install import ( "errors" "fmt" - k8serrors "k8s.io/apimachinery/pkg/api/errors" "os" "strings" "sync" "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + consulChart "github.com/hashicorp/consul-k8s/charts" "github.com/hashicorp/consul-k8s/cli/cmd/common" "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/config" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" helmCLI "helm.sh/helm/v3/pkg/cli" @@ -81,7 +83,7 @@ type Command struct { func (c *Command) init() { // Store all the possible preset values in 'presetList'. Printed in the help message. var presetList []string - for name := range presets { + for name := range config.Presets { presetList = append(presetList, name) } @@ -179,7 +181,6 @@ type helmValues struct { } type globalValues struct { - Image string `yaml:"image"` EnterpriseLicense enterpriseLicense `yaml:"enterpriseLicense"` } @@ -291,7 +292,7 @@ func (c *Command) Run(args []string) int { // If an enterprise license secret was provided check that the secret exists // and that the enterprise Consul image is set. if v.Global.EnterpriseLicense.SecretName != "" { - if err := c.checkValidEnterprise(v.Global.EnterpriseLicense.SecretName, v.Global.Image); err != nil { + if err := c.checkValidEnterprise(v.Global.EnterpriseLicense.SecretName); err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 } @@ -313,7 +314,7 @@ func (c *Command) Run(args []string) int { // Without informing the user, default global.name to consul if it hasn't been set already. We don't allow setting // the release name, and since that is hardcoded to "consul", setting global.name to "consul" makes it so resources // aren't double prefixed with "consul-consul-...". - vals = mergeMaps(convert(globalNameConsul), vals) + vals = MergeMaps(config.Convert(config.GlobalNameConsul), vals) // Dry Run should exit here, no need to actual locate/download the charts. if c.flagDryRun { @@ -454,15 +455,15 @@ func (c *Command) mergeValuesFlagsWithPrecedence(settings *helmCLI.EnvSettings) } if c.flagPreset != defaultPreset { // Note the ordering of the function call, presets have lower precedence than set vals. - presetMap := presets[c.flagPreset].(map[string]interface{}) - vals = mergeMaps(presetMap, vals) + presetMap := config.Presets[c.flagPreset].(map[string]interface{}) + vals = MergeMaps(presetMap, vals) } return vals, err } -// mergeMaps is a helper function used in Run. Merges two maps giving b precedent. +// MergeMaps is a helper function used in Run. Merges two maps giving b precedent. // @source: https://github.com/helm/helm/blob/main/pkg/cli/values/options.go -func mergeMaps(a, b map[string]interface{}) map[string]interface{} { +func MergeMaps(a, b map[string]interface{}) map[string]interface{} { out := make(map[string]interface{}, len(a)) for k, v := range a { out[k] = v @@ -471,7 +472,7 @@ func mergeMaps(a, b map[string]interface{}) map[string]interface{} { if v, ok := v.(map[string]interface{}); ok { if bv, ok := out[k]; ok { if bv, ok := bv.(map[string]interface{}); ok { - out[k] = mergeMaps(bv, v) + out[k] = MergeMaps(bv, v) continue } } @@ -492,7 +493,7 @@ func (c *Command) validateFlags(args []string) error { if len(c.flagValueFiles) != 0 && c.flagPreset != defaultPreset { return fmt.Errorf("Cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) } - if _, ok := presets[c.flagPreset]; c.flagPreset != defaultPreset && !ok { + if _, ok := config.Presets[c.flagPreset]; c.flagPreset != defaultPreset && !ok { return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) } if !validLabel(c.flagNamespace) { @@ -535,8 +536,8 @@ func validLabel(s string) bool { // checkValidEnterprise checks and validates an enterprise installation. // When an enterprise license secret is provided, check that the secret exists -// in the "consul" namespace, and that the enterprise Consul image is provided. -func (c *Command) checkValidEnterprise(secretName string, image string) error { +// in the "consul" namespace. +func (c *Command) checkValidEnterprise(secretName string) error { _, err := c.kubernetes.CoreV1().Secrets(c.flagNamespace).Get(c.Ctx, secretName, metav1.GetOptions{}) if k8serrors.IsNotFound(err) { @@ -544,9 +545,6 @@ func (c *Command) checkValidEnterprise(secretName string, image string) error { } else if err != nil { return fmt.Errorf("error getting the enterprise license secret %q in the %q namespace: %s", secretName, c.flagNamespace, err) } - if !strings.Contains(image, "-ent") { - return fmt.Errorf("enterprise Consul image is not provided when enterprise license secret is set: %s", image) - } - c.UI.Output("Valid enterprise Consul image and secret found.", terminal.WithSuccessStyle()) + c.UI.Output("Valid enterprise Consul secret found.", terminal.WithSuccessStyle()) return nil } diff --git a/cli/cmd/install/install_test.go b/cli/cmd/install/install_test.go index f9e4028d05..ba6608d256 100644 --- a/cli/cmd/install/install_test.go +++ b/cli/cmd/install/install_test.go @@ -203,24 +203,19 @@ func TestCheckValidEnterprise(t *testing.T) { }, } - // Enterprise secret and image are valid. + // Enterprise secret is valid. c.kubernetes.CoreV1().Secrets("consul").Create(context.Background(), secret, metav1.CreateOptions{}) - err := c.checkValidEnterprise(secret.Name, "consul-enterprise:-ent") + err := c.checkValidEnterprise(secret.Name) require.NoError(t, err) - // Enterprise secret provided but not an enterprise image. - err = c.checkValidEnterprise(secret.Name, "consul:") - require.Error(t, err) - require.Contains(t, err.Error(), "enterprise Consul image is not provided") - // Enterprise secret does not exist. - err = c.checkValidEnterprise("consul-unrelated-secret", "consul-enterprise:-ent") + err = c.checkValidEnterprise("consul-unrelated-secret") require.Error(t, err) require.Contains(t, err.Error(), "please make sure that the secret exists") // Enterprise secret exists in a different namespace. c.kubernetes.CoreV1().Secrets("unrelated").Create(context.Background(), secret2, metav1.CreateOptions{}) - err = c.checkValidEnterprise(secret2.Name, "consul-enterprise:-ent") + err = c.checkValidEnterprise(secret2.Name) require.Error(t, err) require.Contains(t, err.Error(), "please make sure that the secret exists") } diff --git a/cli/cmd/install/presets.go b/cli/cmd/install/presets.go deleted file mode 100644 index f901d1e930..0000000000 --- a/cli/cmd/install/presets.go +++ /dev/null @@ -1,68 +0,0 @@ -package install - -import "sigs.k8s.io/yaml" - -const ( - PresetDemo = "demo" - PresetSecure = "secure" -) - -// presets is a map of pre-configured helm values. -var presets = map[string]interface{}{ - PresetDemo: convert(demo), - PresetSecure: convert(secure), -} - -var demo = ` -global: - name: consul - metrics: - enabled: true - enableAgentMetrics: true -connectInject: - enabled: true - metrics: - defaultEnabled: true - defaultEnableMerging: true - enableGatewayMetrics: true -server: - replicas: 1 -controller: - enabled: true -ui: - enabled: true - service: - enabled: true -prometheus: - enabled: true -` - -var secure = ` -global: - name: consul - gossipEncryption: - autoGenerate: true - tls: - enabled: true - enableAutoEncrypt: true - acls: - manageSystemACLs: true -server: - replicas: 1 -connectInject: - enabled: true -controller: - enabled: true -` - -var globalNameConsul = ` -global: - name: consul -` - -// convert is a helper function that converts a YAML string to a map. -func convert(s string) map[string]interface{} { - var m map[string]interface{} - _ = yaml.Unmarshal([]byte(s), &m) - return m -} diff --git a/cli/cmd/upgrade/upgrade.go b/cli/cmd/upgrade/upgrade.go new file mode 100644 index 0000000000..f659961291 --- /dev/null +++ b/cli/cmd/upgrade/upgrade.go @@ -0,0 +1,428 @@ +package upgrade + +import ( + "errors" + "fmt" + "os" + "strings" + "sync" + "time" + + consulChart "github.com/hashicorp/consul-k8s/charts" + "helm.sh/helm/v3/pkg/chart/loader" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + + "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "github.com/hashicorp/consul-k8s/cli/config" + helmCLI "helm.sh/helm/v3/pkg/cli" + + "github.com/hashicorp/consul-k8s/cli/cmd/install" + + "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" +) + +const ( + flagNamePreset = "preset" + defaultPreset = "" + + flagNameConfigFile = "config-file" + flagNameSetStringValues = "set-string" + flagNameSetValues = "set" + flagNameFileValues = "set-file" + + flagNameDryRun = "dry-run" + defaultDryRun = false + + flagNameAutoApprove = "auto-approve" + defaultAutoApprove = false + + flagNamespace = "namespace" + defaultNamespace = "consul" + + flagNameTimeout = "timeout" + defaultTimeout = "10m" + + flagNameVerbose = "verbose" + defaultVerbose = false + + flagNameWait = "wait" + defaultWait = true +) + +type Command struct { + *common.BaseCommand + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagPreset string + flagDryRun bool + flagAutoApprove bool + flagNamespace string + flagValueFiles []string + flagSetStringValues []string + flagSetValues []string + flagFileValues []string + flagTimeout string + timeoutDuration time.Duration + flagVerbose bool + flagWait bool + + flagKubeConfig string + flagKubeContext string + + once sync.Once + help string +} + +func (c *Command) init() { + // Store all the possible preset values in 'presetList'. Printed in the help message. + var presetList []string + for name := range config.Presets { + presetList = append(presetList, name) + } + + c.set = flag.NewSets() + f := c.set.NewSet("Command Options") + f.BoolVar(&flag.BoolVar{ + Name: flagNameAutoApprove, + Target: &c.flagAutoApprove, + Default: defaultAutoApprove, + Usage: "Skip confirmation prompt.", + }) + f.BoolVar(&flag.BoolVar{ + Name: flagNameDryRun, + Target: &c.flagDryRun, + Default: defaultDryRun, + Usage: "Run pre-install checks and display summary of installation.", + }) + f.StringSliceVar(&flag.StringSliceVar{ + Name: flagNameConfigFile, + Aliases: []string{"f"}, + Target: &c.flagValueFiles, + Usage: "Path to a file to customize the installation, such as Consul Helm chart values file. Can be specified multiple times.", + }) + f.StringVar(&flag.StringVar{ + Name: flagNamePreset, + Target: &c.flagPreset, + Default: defaultPreset, + Usage: fmt.Sprintf("Use an installation preset, one of %s. Defaults to none", strings.Join(presetList, ", ")), + }) + f.StringSliceVar(&flag.StringSliceVar{ + Name: flagNameSetValues, + Target: &c.flagSetValues, + Usage: "Set a value to customize. Can be specified multiple times. Supports Consul Helm chart values.", + }) + f.StringSliceVar(&flag.StringSliceVar{ + Name: flagNameFileValues, + Target: &c.flagFileValues, + Usage: "Set a value to customize via a file. The contents of the file will be set as the value. Can be " + + "specified multiple times. Supports Consul Helm chart values.", + }) + f.StringSliceVar(&flag.StringSliceVar{ + Name: flagNameSetStringValues, + Target: &c.flagSetStringValues, + Usage: "Set a string value to customize. Can be specified multiple times. Supports Consul Helm chart values.", + }) + f.StringVar(&flag.StringVar{ + Name: flagNameTimeout, + Target: &c.flagTimeout, + Default: defaultTimeout, + Usage: "Timeout to wait for installation to be ready.", + }) + f.BoolVar(&flag.BoolVar{ + Name: flagNameVerbose, + Aliases: []string{"v"}, + Target: &c.flagVerbose, + Default: defaultVerbose, + Usage: "Output verbose logs from the install command with the status of resources being installed.", + }) + f.BoolVar(&flag.BoolVar{ + Name: flagNameWait, + Target: &c.flagWait, + Default: defaultWait, + Usage: "Determines whether to wait for resources in installation to be ready before exiting command.", + }) + + 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() + + // c.Init() calls the embedded BaseCommand's initialization function. + c.Init() +} + +func (c *Command) Run(args []string) int { + c.once.Do(c.init) + c.Log.ResetNamed("upgrade") + + defer common.CloseWithError(c.BaseCommand) + + if err := c.validateFlags(args); 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() + + // Any overrides by our kubeconfig and kubecontext flags is done here. The Kube client that + // is created will use this command's flags first, then the HELM_KUBECONTEXT environment variable, + // then call out to genericclioptions.ConfigFlag + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + // Setup logger to stream Helm library logs + var uiLogger = func(s string, args ...interface{}) { + logMsg := fmt.Sprintf(s, args...) + + if c.flagVerbose { + // Only output all logs when verbose is enabled + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } else { + // When verbose is not enabled, output all logs except not ready messages for resources + if !strings.Contains(logMsg, "not ready") { + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } + } + } + + // Set up the kubernetes client 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. + if c.kubernetes == nil { + restConfig, err := settings.RESTClientGetter().ToRESTConfig() + if err != nil { + c.UI.Output("Retrieving Kubernetes auth: %v", err, terminal.WithErrorStyle()) + return 1 + } + c.kubernetes, err = kubernetes.NewForConfig(restConfig) + if err != nil { + c.UI.Output("Initializing Kubernetes client: %v", err, terminal.WithErrorStyle()) + return 1 + } + } + + c.UI.Output("Pre-Upgrade Checks", terminal.WithHeaderStyle()) + + // Note the logic here, common's CheckForInstallations function returns an error if + // the release is not found. In `upgrade` we should indeed error if a user doesn't currently have a release. + var foundNamespace string + if name, ns, err := common.CheckForInstallations(settings, uiLogger); err != nil { + c.UI.Output("could not find existing Consul installation - run `consul-k8s install`") + return 1 + } else { + c.UI.Output("Existing installation found.", terminal.WithSuccessStyle()) + c.UI.Output("Name: %s", name, terminal.WithInfoStyle()) + c.UI.Output("Namespace: %s", ns, terminal.WithInfoStyle()) + + foundNamespace = ns + } + + // Handle preset, value files, and set values logic. + vals, err := c.mergeValuesFlagsWithPrecedence(settings) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + valuesYaml, err := yaml.Marshal(vals) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Print out the upgrade summary. + if !c.flagAutoApprove { + c.UI.Output("Consul Upgrade Summary", terminal.WithHeaderStyle()) + c.UI.Output("Installation name: %s", common.DefaultReleaseName, terminal.WithInfoStyle()) + c.UI.Output("Namespace: %s", foundNamespace, terminal.WithInfoStyle()) + + if len(vals) == 0 { + c.UI.Output("Overrides: "+string(valuesYaml), terminal.WithInfoStyle()) + } else { + c.UI.Output("Overrides:"+"\n"+string(valuesYaml), terminal.WithInfoStyle()) + } + } + + // Without informing the user, default global.name to consul if it hasn't been set already. We don't allow setting + // the release name, and since that is hardcoded to "consul", setting global.name to "consul" makes it so resources + // aren't double prefixed with "consul-consul-...". + vals = install.MergeMaps(config.Convert(config.GlobalNameConsul), vals) + + if !c.flagAutoApprove && !c.flagDryRun { + confirmation, err := c.UI.Input(&terminal.Input{ + Prompt: "Proceed with upgrade? (y/N)", + Style: terminal.InfoStyle, + Secret: false, + }) + + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + if common.Abort(confirmation) { + c.UI.Output("Upgrade aborted. To learn how to customize your upgrade, run:\nconsul-k8s upgrade --help", terminal.WithInfoStyle()) + return 1 + } + } + + if !c.flagDryRun { + c.UI.Output("Running Upgrade", terminal.WithHeaderStyle()) + } else { + c.UI.Output("Performing Dry Upgrade", terminal.WithHeaderStyle()) + } + + // Setup action configuration for Helm Go SDK function calls. + actionConfig := new(action.Configuration) + actionConfig, err = common.InitActionConfig(actionConfig, foundNamespace, settings, uiLogger) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Setup the upgrade action. + upgrade := action.NewUpgrade(actionConfig) + upgrade.Namespace = foundNamespace + upgrade.DryRun = c.flagDryRun + upgrade.Wait = c.flagWait + upgrade.Timeout = c.timeoutDuration + + // Read the embedded chart files into []*loader.BufferedFile. + chartFiles, err := common.ReadChartFiles(consulChart.ConsulHelmChart, common.TopLevelChartDirName) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Create a *chart.Chart object from the files to run the installation from. + chart, err := loader.LoadFiles(chartFiles) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + c.UI.Output("Downloaded charts", terminal.WithSuccessStyle()) + + // Run the install. + re, err := upgrade.Run(common.DefaultReleaseName, chart, vals) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Dry Run should exit here, printing the release's config. + if c.flagDryRun { + configYaml, err := yaml.Marshal(re.Config) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if len(re.Config) == 0 { + c.UI.Output("Config: "+string(configYaml), terminal.WithInfoStyle()) + } else { + c.UI.Output("Config:"+"\n"+string(configYaml), terminal.WithInfoStyle()) + } + c.UI.Output("Dry run complete - upgrade can proceed.", terminal.WithSuccessStyle()) + return 0 + } + + c.UI.Output("Upgraded Consul into namespace %q", foundNamespace, terminal.WithSuccessStyle()) + + return 0 +} + +// validateFlags checks that the user's provided flags are valid. +func (c *Command) validateFlags(args []string) error { + if err := c.set.Parse(args); err != nil { + return err + } + if len(c.set.Args()) > 0 { + return errors.New("should have no non-flag arguments") + } + if len(c.flagValueFiles) != 0 && c.flagPreset != defaultPreset { + return fmt.Errorf("cannot set both -%s and -%s", flagNameConfigFile, flagNamePreset) + } + if _, ok := config.Presets[c.flagPreset]; c.flagPreset != defaultPreset && !ok { + return fmt.Errorf("'%s' is not a valid preset", c.flagPreset) + } + duration, err := time.ParseDuration(c.flagTimeout) + if err != nil { + return fmt.Errorf("unable to parse -%s: %s", flagNameTimeout, err) + } + c.timeoutDuration = duration + if len(c.flagValueFiles) != 0 { + for _, filename := range c.flagValueFiles { + if _, err := os.Stat(filename); err != nil && os.IsNotExist(err) { + return fmt.Errorf("file '%s' does not exist", filename) + } + } + } + + if c.flagDryRun { + c.UI.Output("Performing dry run installation.", terminal.WithInfoStyle()) + } + return nil +} + +// mergeValuesFlagsWithPrecedence is responsible for merging all the values to determine the values file for the +// installation based on the following precedence order from lowest to highest: +// 1. -preset +// 2. -f values-file +// 3. -set +// 4. -set-string +// 5. -set-file +// For example, -set-file will override a value provided via -set. +// Within each of these groups the rightmost flag value has the highest precedence. +func (c *Command) mergeValuesFlagsWithPrecedence(settings *helmCLI.EnvSettings) (map[string]interface{}, error) { + p := getter.All(settings) + v := &values.Options{ + ValueFiles: c.flagValueFiles, + StringValues: c.flagSetStringValues, + Values: c.flagSetValues, + FileValues: c.flagFileValues, + } + vals, err := v.MergeValues(p) + if err != nil { + return nil, fmt.Errorf("error merging values: %s", err) + } + if c.flagPreset != defaultPreset { + // Note the ordering of the function call, presets have lower precedence than set vals. + presetMap := config.Presets[c.flagPreset].(map[string]interface{}) + vals = install.MergeMaps(presetMap, vals) + } + return vals, err +} + +func (c *Command) Help() string { + c.once.Do(c.init) + s := "Usage: consul-k8s upgrade [flags]" + "\n" + "Upgrade Consul from an existing installation." + "\n" + return s + "\n" + c.help +} + +func (c *Command) Synopsis() string { + return "Upgrade Consul on Kubernetes." +} diff --git a/cli/cmd/upgrade/upgrade_test.go b/cli/cmd/upgrade/upgrade_test.go new file mode 100644 index 0000000000..cd47928f9f --- /dev/null +++ b/cli/cmd/upgrade/upgrade_test.go @@ -0,0 +1,72 @@ +package upgrade + +import ( + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/go-hclog" +) + +// TestValidateFlags tests the validate flags function. +func TestValidateFlags(t *testing.T) { + // The following cases should all error, if they fail to this test fails. + testCases := []struct { + description string + input []string + }{ + { + "Should disallow non-flag arguments.", + []string{"foo", "-auto-approve"}, + }, + { + "Should disallow specifying both values file AND presets.", + []string{"-f='f.txt'", "-preset=demo"}, + }, + { + "Should error on invalid presets.", + []string{"-preset=foo"}, + }, + { + "Should error on invalid timeout.", + []string{"-timeout=invalid-timeout"}, + }, + { + "Should error on an invalid namespace.", + []string{"-namespace=\" nsWithSpace\""}, + }, + { + "Should have errored on a non-existant file.", + []string{"-f=\"does_not_exist.txt\""}, + }, + } + + for _, testCase := range testCases { + c := getInitializedCommand(t) + t.Run(testCase.description, func(t *testing.T) { + if err := c.validateFlags(testCase.input); err == nil { + t.Errorf("Test case should have failed.") + } + }) + } +} + +// getInitializedCommand sets up a command struct for tests. +func getInitializedCommand(t *testing.T) *Command { + t.Helper() + log := hclog.New(&hclog.LoggerOptions{ + Name: "cli", + Level: hclog.Info, + Output: os.Stdout, + }) + + baseCommand := &common.BaseCommand{ + Log: log, + } + + c := &Command{ + BaseCommand: baseCommand, + } + c.init() + return c +} diff --git a/cli/commands.go b/cli/commands.go index 50da5806d4..17726ac5aa 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/status" "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" "github.com/hashicorp/consul-k8s/cli/version" "github.com/hashicorp/go-hclog" @@ -36,6 +37,11 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "upgrade": func() (cli.Command, error) { + return &upgrade.Command{ + BaseCommand: baseCommand, + }, nil + }, "version": func() (cli.Command, error) { return &cmdversion.Command{ BaseCommand: baseCommand, diff --git a/cli/config/presets.go b/cli/config/presets.go new file mode 100644 index 0000000000..557cb24363 --- /dev/null +++ b/cli/config/presets.go @@ -0,0 +1,80 @@ +package config + +import "sigs.k8s.io/yaml" + +const ( + PresetDemo = "demo" + PresetSecure = "secure" +) + +// Presets is a map of pre-configured helm values. +var Presets = map[string]interface{}{ + PresetDemo: Convert(demo), + PresetSecure: Convert(secure), +} + +const demo = ` +global: + name: consul +connectInject: + enabled: true +server: + replicas: 1 + bootstrapExpect: 1 +` + +// TODO: I don't know why the following hangs for me. +// TODO: This was hanging for Saad. I will look into it. +//var demo = ` +//global: +// name: consul +// metrics: +// enabled: true +// enableAgentMetrics: true +//connectInject: +// enabled: true +// metrics: +// defaultEnabled: true +// defaultEnableMerging: true +// enableGatewayMetrics: true +//server: +// replicas: 1 +//controller: +// enabled: true +//ui: +// enabled: true +// service: +// enabled: true +//prometheus: +// enabled: true +//` + +const secure = ` +global: + name: consul + gossipEncryption: + autoGenerate: true + tls: + enabled: true + enableAutoEncrypt: true + acls: + manageSystemACLs: true +server: + replicas: 1 +connectInject: + enabled: true +controller: + enabled: true +` + +const GlobalNameConsul = ` +global: + name: consul +` + +// convert is a helper function that converts a YAML string to a map. +func Convert(s string) map[string]interface{} { + var m map[string]interface{} + _ = yaml.Unmarshal([]byte(s), &m) + return m +} diff --git a/cli/version/version.go b/cli/version/version.go index 52fb3c2b9c..44f7aca672 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 = "0.37.0" + Version = "0.38.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/PROJECT b/control-plane/PROJECT index 9e26de1f52..16d303458c 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -56,7 +56,7 @@ resources: - controller: true domain: hashicorp.com group: consul - kind: PartitionExport + kind: ExportedServices path: github.com/hashicorp/consul-k8s/api/v1alpha1 version: v1alpha1 version: "3" diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index a44f527472..7c761b6477 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -8,7 +8,7 @@ const ( ServiceRouter string = "servicerouter" ServiceSplitter string = "servicesplitter" ServiceIntentions string = "serviceintentions" - PartitionExports string = "partitionexports" + ExportedServices string = "exportedservices" IngressGateway string = "ingressgateway" TerminatingGateway string = "terminatinggateway" diff --git a/control-plane/api/v1alpha1/partitionexports_types.go b/control-plane/api/v1alpha1/exportedservices_types.go similarity index 72% rename from control-plane/api/v1alpha1/partitionexports_types.go rename to control-plane/api/v1alpha1/exportedservices_types.go index 9fa01fa333..5c1e337c2d 100644 --- a/control-plane/api/v1alpha1/partitionexports_types.go +++ b/control-plane/api/v1alpha1/exportedservices_types.go @@ -16,38 +16,38 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ) -const PartitionExportsKubeKind = "partitionexports" +const ExportedServicesKubeKind = "exportedservices" func init() { - SchemeBuilder.Register(&PartitionExports{}, &PartitionExportsList{}) + SchemeBuilder.Register(&ExportedServices{}, &ExportedServicesList{}) } //+kubebuilder:object:root=true //+kubebuilder:subresource:status -// PartitionExports is the Schema for the partitionexports API +// ExportedServices is the Schema for the exportedservices 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 PartitionExports struct { +type ExportedServices struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec PartitionExportsSpec `json:"spec,omitempty"` + Spec ExportedServicesSpec `json:"spec,omitempty"` Status `json:"status,omitempty"` } //+kubebuilder:object:root=true -// PartitionExportsList contains a list of PartitionExports -type PartitionExportsList struct { +// ExportedServicesList contains a list of ExportedServices +type ExportedServicesList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []PartitionExports `json:"items"` + Items []ExportedServices `json:"items"` } -// PartitionExportsSpec defines the desired state of PartitionExports -type PartitionExportsSpec struct { +// ExportedServicesSpec defines the desired state of ExportedServices +type ExportedServicesSpec struct { // Services is a list of services to be exported and the list of partitions // to expose them to. Services []ExportedService `json:"services,omitempty"` @@ -72,15 +72,15 @@ type ServiceConsumer struct { Partition string `json:"partition,omitempty"` } -func (in *PartitionExports) GetObjectMeta() metav1.ObjectMeta { +func (in *ExportedServices) GetObjectMeta() metav1.ObjectMeta { return in.ObjectMeta } -func (in *PartitionExports) AddFinalizer(name string) { +func (in *ExportedServices) AddFinalizer(name string) { in.ObjectMeta.Finalizers = append(in.Finalizers(), name) } -func (in *PartitionExports) RemoveFinalizer(name string) { +func (in *ExportedServices) RemoveFinalizer(name string) { var newFinalizers []string for _, oldF := range in.Finalizers() { if oldF != name { @@ -90,35 +90,35 @@ func (in *PartitionExports) RemoveFinalizer(name string) { in.ObjectMeta.Finalizers = newFinalizers } -func (in *PartitionExports) Finalizers() []string { +func (in *ExportedServices) Finalizers() []string { return in.ObjectMeta.Finalizers } -func (in *PartitionExports) ConsulKind() string { - return capi.PartitionExports +func (in *ExportedServices) ConsulKind() string { + return capi.ExportedServices } -func (in *PartitionExports) ConsulGlobalResource() bool { +func (in *ExportedServices) ConsulGlobalResource() bool { return true } -func (in *PartitionExports) ConsulMirroringNS() string { +func (in *ExportedServices) ConsulMirroringNS() string { return common.DefaultConsulNamespace } -func (in *PartitionExports) KubeKind() string { - return PartitionExportsKubeKind +func (in *ExportedServices) KubeKind() string { + return ExportedServicesKubeKind } -func (in *PartitionExports) ConsulName() string { +func (in *ExportedServices) ConsulName() string { return in.ObjectMeta.Name } -func (in *PartitionExports) KubernetesName() string { +func (in *ExportedServices) KubernetesName() string { return in.ObjectMeta.Name } -func (in *PartitionExports) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { +func (in *ExportedServices) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { in.Status.Conditions = Conditions{ { Type: ConditionSynced, @@ -130,11 +130,11 @@ func (in *PartitionExports) SetSyncedCondition(status corev1.ConditionStatus, re } } -func (in *PartitionExports) SetLastSyncedTime(time *metav1.Time) { +func (in *ExportedServices) SetLastSyncedTime(time *metav1.Time) { in.Status.LastSyncedTime = time } -func (in *PartitionExports) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { +func (in *ExportedServices) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { cond := in.Status.GetCondition(ConditionSynced) if cond == nil { return corev1.ConditionUnknown, "", "" @@ -142,7 +142,7 @@ func (in *PartitionExports) SyncedCondition() (status corev1.ConditionStatus, re return cond.Status, cond.Reason, cond.Message } -func (in *PartitionExports) SyncedConditionStatus() corev1.ConditionStatus { +func (in *ExportedServices) SyncedConditionStatus() corev1.ConditionStatus { cond := in.Status.GetCondition(ConditionSynced) if cond == nil { return corev1.ConditionUnknown @@ -150,12 +150,12 @@ func (in *PartitionExports) SyncedConditionStatus() corev1.ConditionStatus { return cond.Status } -func (in *PartitionExports) ToConsul(datacenter string) api.ConfigEntry { +func (in *ExportedServices) ToConsul(datacenter string) api.ConfigEntry { var services []capi.ExportedService for _, service := range in.Spec.Services { services = append(services, service.toConsul()) } - return &capi.PartitionExportsConfigEntry{ + return &capi.ExportedServicesConfigEntry{ Name: in.Name, Services: services, Meta: meta(datacenter), @@ -174,23 +174,23 @@ func (in *ExportedService) toConsul() capi.ExportedService { } } -func (in *PartitionExports) MatchesConsul(candidate api.ConfigEntry) bool { - configEntry, ok := candidate.(*capi.PartitionExportsConfigEntry) +func (in *ExportedServices) MatchesConsul(candidate api.ConfigEntry) bool { + configEntry, ok := candidate.(*capi.ExportedServicesConfigEntry) if !ok { return false } // 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.PartitionExportsConfigEntry{}, "Partition", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) + return cmp.Equal(in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.ExportedServicesConfigEntry{}, "Partition", "Meta", "ModifyIndex", "CreateIndex"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty()) } -func (in *PartitionExports) Validate(consulMeta common.ConsulMeta) error { +func (in *ExportedServices) Validate(consulMeta common.ConsulMeta) error { var errs field.ErrorList if !consulMeta.PartitionsEnabled { return apierrors.NewForbidden( - schema.GroupResource{Group: ConsulHashicorpGroup, Resource: common.PartitionExports}, + schema.GroupResource{Group: ConsulHashicorpGroup, Resource: common.ExportedServices}, in.KubernetesName(), - errors.New("Consul Enterprise Admin Partitions must be enabled to create PartitionExports")) + errors.New("Consul Enterprise Admin Partitions must be enabled to create ExportedServices")) } if in.Name != consulMeta.Partition { errs = append(errs, field.Invalid(field.NewPath("name"), in.Name, fmt.Sprintf(`%s resource name must be the same name as the partition, "%s"`, in.KubeKind(), consulMeta.Partition))) @@ -205,7 +205,7 @@ func (in *PartitionExports) Validate(consulMeta common.ConsulMeta) error { } if len(errs) > 0 { return apierrors.NewInvalid( - schema.GroupKind{Group: ConsulHashicorpGroup, Kind: PartitionExportsKubeKind}, + schema.GroupKind{Group: ConsulHashicorpGroup, Kind: ExportedServicesKubeKind}, in.KubernetesName(), errs) } return nil @@ -218,5 +218,5 @@ func (in *ExportedService) validate(path *field.Path) *field.Error { return nil } -func (in *PartitionExports) DefaultNamespaceFields(_ common.ConsulMeta) { +func (in *ExportedServices) DefaultNamespaceFields(_ common.ConsulMeta) { } diff --git a/control-plane/api/v1alpha1/partitionexports_types_test.go b/control-plane/api/v1alpha1/exportedservices_types_test.go similarity index 61% rename from control-plane/api/v1alpha1/partitionexports_types_test.go rename to control-plane/api/v1alpha1/exportedservices_types_test.go index dc0898da66..b35f27cca2 100644 --- a/control-plane/api/v1alpha1/partitionexports_types_test.go +++ b/control-plane/api/v1alpha1/exportedservices_types_test.go @@ -12,20 +12,20 @@ import ( ) // Test MatchesConsul for cases that should return true. -func TestPartitionExports_MatchesConsul(t *testing.T) { +func TestExportedServices_MatchesConsul(t *testing.T) { cases := map[string]struct { - Ours PartitionExports + Ours ExportedServices Theirs capi.ConfigEntry Matches bool }{ "empty fields matches": { - Ours: PartitionExports{ + Ours: ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, }, - Spec: PartitionExportsSpec{}, + Spec: ExportedServicesSpec{}, }, - Theirs: &capi.PartitionExportsConfigEntry{ + Theirs: &capi.ExportedServicesConfigEntry{ Name: common.DefaultConsulPartition, CreateIndex: 1, ModifyIndex: 2, @@ -37,11 +37,11 @@ func TestPartitionExports_MatchesConsul(t *testing.T) { Matches: true, }, "all fields set matches": { - Ours: PartitionExports{ + Ours: ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service-frontend", @@ -70,7 +70,7 @@ func TestPartitionExports_MatchesConsul(t *testing.T) { }, }, }, - Theirs: &capi.PartitionExportsConfigEntry{ + Theirs: &capi.ExportedServicesConfigEntry{ Name: common.DefaultConsulPartition, Services: []capi.ExportedService{ { @@ -108,15 +108,15 @@ func TestPartitionExports_MatchesConsul(t *testing.T) { Matches: true, }, "mismatched types does not match": { - Ours: PartitionExports{ + Ours: ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, }, - Spec: PartitionExportsSpec{}, + Spec: ExportedServicesSpec{}, }, Theirs: &capi.ServiceConfigEntry{ Name: common.DefaultConsulPartition, - Kind: capi.PartitionExports, + Kind: capi.ExportedServices, }, Matches: false, }, @@ -128,19 +128,19 @@ func TestPartitionExports_MatchesConsul(t *testing.T) { } } -func TestPartitionExports_ToConsul(t *testing.T) { +func TestExportedServices_ToConsul(t *testing.T) { cases := map[string]struct { - Ours PartitionExports - Exp *capi.PartitionExportsConfigEntry + Ours ExportedServices + Exp *capi.ExportedServicesConfigEntry }{ "empty fields": { - Ours: PartitionExports{ + Ours: ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, }, - Spec: PartitionExportsSpec{}, + Spec: ExportedServicesSpec{}, }, - Exp: &capi.PartitionExportsConfigEntry{ + Exp: &capi.ExportedServicesConfigEntry{ Name: common.DefaultConsulPartition, Meta: map[string]string{ common.SourceKey: common.SourceValue, @@ -149,11 +149,11 @@ func TestPartitionExports_ToConsul(t *testing.T) { }, }, "every field set": { - Ours: PartitionExports{ + Ours: ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: common.DefaultConsulPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service-frontend", @@ -182,7 +182,7 @@ func TestPartitionExports_ToConsul(t *testing.T) { }, }, }, - Exp: &capi.PartitionExportsConfigEntry{ + Exp: &capi.ExportedServicesConfigEntry{ Name: common.DefaultConsulPartition, Services: []capi.ExportedService{ { @@ -220,49 +220,49 @@ func TestPartitionExports_ToConsul(t *testing.T) { for name, c := range cases { t.Run(name, func(t *testing.T) { act := c.Ours.ToConsul("datacenter") - partitionExports, ok := act.(*capi.PartitionExportsConfigEntry) + exportedServices, ok := act.(*capi.ExportedServicesConfigEntry) require.True(t, ok, "could not cast") - require.Equal(t, c.Exp, partitionExports) + require.Equal(t, c.Exp, exportedServices) }) } } -func TestPartitionExports_AddFinalizer(t *testing.T) { - partitionExports := &PartitionExports{} - partitionExports.AddFinalizer("finalizer") - require.Equal(t, []string{"finalizer"}, partitionExports.ObjectMeta.Finalizers) +func TestExportedServices_AddFinalizer(t *testing.T) { + exportedServices := &ExportedServices{} + exportedServices.AddFinalizer("finalizer") + require.Equal(t, []string{"finalizer"}, exportedServices.ObjectMeta.Finalizers) } -func TestPartitionExports_RemoveFinalizer(t *testing.T) { - partitionExports := &PartitionExports{ +func TestExportedServices_RemoveFinalizer(t *testing.T) { + exportedServices := &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Finalizers: []string{"f1", "f2"}, }, } - partitionExports.RemoveFinalizer("f1") - require.Equal(t, []string{"f2"}, partitionExports.ObjectMeta.Finalizers) + exportedServices.RemoveFinalizer("f1") + require.Equal(t, []string{"f2"}, exportedServices.ObjectMeta.Finalizers) } -func TestPartitionExports_SetSyncedCondition(t *testing.T) { - partitionExports := &PartitionExports{} - partitionExports.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") +func TestExportedServices_SetSyncedCondition(t *testing.T) { + exportedServices := &ExportedServices{} + exportedServices.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") - require.Equal(t, corev1.ConditionTrue, partitionExports.Status.Conditions[0].Status) - require.Equal(t, "reason", partitionExports.Status.Conditions[0].Reason) - require.Equal(t, "message", partitionExports.Status.Conditions[0].Message) + require.Equal(t, corev1.ConditionTrue, exportedServices.Status.Conditions[0].Status) + require.Equal(t, "reason", exportedServices.Status.Conditions[0].Reason) + require.Equal(t, "message", exportedServices.Status.Conditions[0].Message) now := metav1.Now() - require.True(t, partitionExports.Status.Conditions[0].LastTransitionTime.Before(&now)) + require.True(t, exportedServices.Status.Conditions[0].LastTransitionTime.Before(&now)) } -func TestPartitionExports_SetLastSyncedTime(t *testing.T) { - partitionExports := &PartitionExports{} +func TestExportedServices_SetLastSyncedTime(t *testing.T) { + exportedServices := &ExportedServices{} syncedTime := metav1.NewTime(time.Now()) - partitionExports.SetLastSyncedTime(&syncedTime) + exportedServices.SetLastSyncedTime(&syncedTime) - require.Equal(t, &syncedTime, partitionExports.Status.LastSyncedTime) + require.Equal(t, &syncedTime, exportedServices.Status.LastSyncedTime) } -func TestPartitionExports_GetSyncedConditionStatus(t *testing.T) { +func TestExportedServices_GetSyncedConditionStatus(t *testing.T) { cases := []corev1.ConditionStatus{ corev1.ConditionUnknown, corev1.ConditionFalse, @@ -270,7 +270,7 @@ func TestPartitionExports_GetSyncedConditionStatus(t *testing.T) { } for _, status := range cases { t.Run(string(status), func(t *testing.T) { - partitionExports := &PartitionExports{ + exportedServices := &ExportedServices{ Status: Status{ Conditions: []Condition{{ Type: ConditionSynced, @@ -279,57 +279,57 @@ func TestPartitionExports_GetSyncedConditionStatus(t *testing.T) { }, } - require.Equal(t, status, partitionExports.SyncedConditionStatus()) + require.Equal(t, status, exportedServices.SyncedConditionStatus()) }) } } -func TestPartitionExports_GetConditionWhenStatusNil(t *testing.T) { - require.Nil(t, (&PartitionExports{}).GetCondition(ConditionSynced)) +func TestExportedServices_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&ExportedServices{}).GetCondition(ConditionSynced)) } -func TestPartitionExports_SyncedConditionStatusWhenStatusNil(t *testing.T) { - require.Equal(t, corev1.ConditionUnknown, (&PartitionExports{}).SyncedConditionStatus()) +func TestExportedServices_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&ExportedServices{}).SyncedConditionStatus()) } -func TestPartitionExports_SyncedConditionWhenStatusNil(t *testing.T) { - status, reason, message := (&PartitionExports{}).SyncedCondition() +func TestExportedServices_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&ExportedServices{}).SyncedCondition() require.Equal(t, corev1.ConditionUnknown, status) require.Equal(t, "", reason) require.Equal(t, "", message) } -func TestPartitionExports_ConsulKind(t *testing.T) { - require.Equal(t, capi.PartitionExports, (&PartitionExports{}).ConsulKind()) +func TestExportedServices_ConsulKind(t *testing.T) { + require.Equal(t, capi.ExportedServices, (&ExportedServices{}).ConsulKind()) } -func TestPartitionExports_KubeKind(t *testing.T) { - require.Equal(t, "partitionexports", (&PartitionExports{}).KubeKind()) +func TestExportedServices_KubeKind(t *testing.T) { + require.Equal(t, "exportedservices", (&ExportedServices{}).KubeKind()) } -func TestPartitionExports_ConsulName(t *testing.T) { - require.Equal(t, "foo", (&PartitionExports{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) +func TestExportedServices_ConsulName(t *testing.T) { + require.Equal(t, "foo", (&ExportedServices{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).ConsulName()) } -func TestPartitionExports_KubernetesName(t *testing.T) { - require.Equal(t, "foo", (&PartitionExports{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) +func TestExportedServices_KubernetesName(t *testing.T) { + require.Equal(t, "foo", (&ExportedServices{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}).KubernetesName()) } -func TestPartitionExports_ConsulNamespace(t *testing.T) { - require.Equal(t, common.DefaultConsulNamespace, (&PartitionExports{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) +func TestExportedServices_ConsulNamespace(t *testing.T) { + require.Equal(t, common.DefaultConsulNamespace, (&ExportedServices{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}}).ConsulMirroringNS()) } -func TestPartitionExports_ConsulGlobalResource(t *testing.T) { - require.True(t, (&PartitionExports{}).ConsulGlobalResource()) +func TestExportedServices_ConsulGlobalResource(t *testing.T) { + require.True(t, (&ExportedServices{}).ConsulGlobalResource()) } -func TestPartitionExports_ObjectMeta(t *testing.T) { +func TestExportedServices_ObjectMeta(t *testing.T) { meta := metav1.ObjectMeta{ Name: "name", Namespace: "namespace", } - partitionExports := &PartitionExports{ + exportedServices := &ExportedServices{ ObjectMeta: meta, } - require.Equal(t, meta, partitionExports.GetObjectMeta()) + require.Equal(t, meta, exportedServices.GetObjectMeta()) } diff --git a/control-plane/api/v1alpha1/partitionexports_webhook.go b/control-plane/api/v1alpha1/exportedservices_webhook.go similarity index 75% rename from control-plane/api/v1alpha1/partitionexports_webhook.go rename to control-plane/api/v1alpha1/exportedservices_webhook.go index 4311055289..d80062e958 100644 --- a/control-plane/api/v1alpha1/partitionexports_webhook.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook.go @@ -15,7 +15,7 @@ import ( // +kubebuilder:object:generate=false -type PartitionExportsWebhook struct { +type ExportedServicesWebhook struct { client.Client ConsulClient *capi.Client Logger logr.Logger @@ -30,11 +30,11 @@ type PartitionExportsWebhook struct { // 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-partitionexports,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=partitionexports,versions=v1alpha1,name=mutate-partitionexports.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v1alpha1-exportedservices,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=exportedservices,versions=v1alpha1,name=mutate-exportedservices.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 -func (v *PartitionExportsWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { - var exports PartitionExports - var exportsList PartitionExportsList +func (v *ExportedServicesWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var exports ExportedServices + var exportsList ExportedServicesList err := v.decoder.Decode(req, &exports) if err != nil { return admission.Errored(http.StatusBadRequest, err) @@ -49,7 +49,7 @@ func (v *PartitionExportsWebhook) Handle(ctx context.Context, req admission.Requ if len(exportsList.Items) > 0 { return admission.Errored(http.StatusBadRequest, - fmt.Errorf("%s resource already defined - only one partitionexports entry is supported per Kubernetes cluster", + fmt.Errorf("%s resource already defined - only one exportedservices entry is supported per Kubernetes cluster", exports.KubeKind())) } } @@ -61,7 +61,7 @@ func (v *PartitionExportsWebhook) Handle(ctx context.Context, req admission.Requ return admission.Allowed(fmt.Sprintf("valid %s request", exports.KubeKind())) } -func (v *PartitionExportsWebhook) InjectDecoder(d *admission.Decoder) error { +func (v *ExportedServicesWebhook) InjectDecoder(d *admission.Decoder) error { v.decoder = d return nil } diff --git a/control-plane/api/v1alpha1/partitionexports_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go similarity index 79% rename from control-plane/api/v1alpha1/partitionexports_webhook_test.go rename to control-plane/api/v1alpha1/exportedservices_webhook_test.go index caacf3dfbc..9c4b79b13d 100644 --- a/control-plane/api/v1alpha1/partitionexports_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -15,24 +15,24 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) -func TestValidatePartitionExports(t *testing.T) { +func TestValidateExportedServices(t *testing.T) { otherNS := "other" otherPartition := "other" cases := map[string]struct { existingResources []runtime.Object - newResource *PartitionExports + newResource *ExportedServices consulMeta common.ConsulMeta expAllow bool expErrMessage string }{ "no duplicates, valid": { existingResources: nil, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service", @@ -48,17 +48,17 @@ func TestValidatePartitionExports(t *testing.T) { }, expAllow: true, }, - "partitionexports exists": { - existingResources: []runtime.Object{&PartitionExports{ + "exportedservices exists": { + existingResources: []runtime.Object{&ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, }}, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service", @@ -73,15 +73,15 @@ func TestValidatePartitionExports(t *testing.T) { Partition: otherPartition, }, expAllow: false, - expErrMessage: "partitionexports resource already defined - only one partitionexports entry is supported per Kubernetes cluster", + expErrMessage: "exportedservices resource already defined - only one exportedservices entry is supported per Kubernetes cluster", }, "name not partition name": { existingResources: []runtime.Object{}, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: "local", }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service", @@ -96,15 +96,15 @@ func TestValidatePartitionExports(t *testing.T) { Partition: otherPartition, }, expAllow: false, - expErrMessage: "partitionexports.consul.hashicorp.com \"local\" is invalid: name: Invalid value: \"local\": partitionexports resource name must be the same name as the partition, \"other\"", + expErrMessage: "exportedservices.consul.hashicorp.com \"local\" is invalid: name: Invalid value: \"local\": exportedservices resource name must be the same name as the partition, \"other\"", }, "partitions disabled": { existingResources: []runtime.Object{}, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service", @@ -119,15 +119,15 @@ func TestValidatePartitionExports(t *testing.T) { Partition: "", }, expAllow: false, - expErrMessage: "partitionexports.consul.hashicorp.com \"other\" is forbidden: Consul Enterprise Admin Partitions must be enabled to create PartitionExports", + expErrMessage: "exportedservices.consul.hashicorp.com \"other\" is forbidden: Consul Enterprise Admin Partitions must be enabled to create ExportedServices", }, "no services": { existingResources: []runtime.Object{}, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{}, }, }, @@ -136,15 +136,15 @@ func TestValidatePartitionExports(t *testing.T) { Partition: otherPartition, }, expAllow: false, - expErrMessage: "partitionexports.consul.hashicorp.com \"other\" is invalid: spec.services: Invalid value: []v1alpha1.ExportedService(nil): at least one service must be exported", + expErrMessage: "exportedservices.consul.hashicorp.com \"other\" is invalid: spec.services: Invalid value: []v1alpha1.ExportedService(nil): at least one service must be exported", }, "service with no consumers": { existingResources: []runtime.Object{}, - newResource: &PartitionExports{ + newResource: &ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: otherPartition, }, - Spec: PartitionExportsSpec{ + Spec: ExportedServicesSpec{ Services: []ExportedService{ { Name: "service", @@ -159,7 +159,7 @@ func TestValidatePartitionExports(t *testing.T) { Partition: otherPartition, }, expAllow: false, - expErrMessage: "partitionexports.consul.hashicorp.com \"other\" is invalid: spec.services[0]: Invalid value: []v1alpha1.ServiceConsumer(nil): service must have at least 1 consumer.", + expErrMessage: "exportedservices.consul.hashicorp.com \"other\" is invalid: spec.services[0]: Invalid value: []v1alpha1.ServiceConsumer(nil): service must have at least 1 consumer.", }, } for name, c := range cases { @@ -168,12 +168,12 @@ func TestValidatePartitionExports(t *testing.T) { marshalledRequestObject, err := json.Marshal(c.newResource) require.NoError(t, err) s := runtime.NewScheme() - s.AddKnownTypes(GroupVersion, &PartitionExports{}, &PartitionExportsList{}) + s.AddKnownTypes(GroupVersion, &ExportedServices{}, &ExportedServicesList{}) client := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(c.existingResources...).Build() decoder, err := admission.NewDecoder(s) require.NoError(t, err) - validator := &PartitionExportsWebhook{ + validator := &ExportedServicesWebhook{ Client: client, ConsulClient: nil, Logger: logrtest.TestLogger{T: t}, diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index f0ea92d300..8def0f2fa4 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -81,9 +81,12 @@ type ServiceResolverRedirect struct { // of one defined as that service's DefaultSubset If empty the default // subset is used. ServiceSubset string `json:"serviceSubset,omitempty"` - // Namespace is the namespace to resolve the service from instead of the - // current one. + // Namespace is the Consul namespace to resolve the service from instead of + // the current namespace. If empty the current namespace is assumed. Namespace string `json:"namespace,omitempty"` + // Partition is the Consul partition to resolve the service from instead of + // the current partition. If empty the current partition is assumed. + Partition string `json:"partition,omitempty"` // Datacenter is the datacenter to resolve the service from instead of the // current one. Datacenter string `json:"datacenter,omitempty"` @@ -301,7 +304,7 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error { errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...) - errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) + errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { return apierrors.NewInvalid( @@ -435,10 +438,10 @@ func (in *CookieConfig) validate(path *field.Path) *field.Error { return nil } -func (in *ServiceResolver) validateNamespaces(namespacesEnabled bool) field.ErrorList { +func (in *ServiceResolver) validateEnterprise(consulMeta common.ConsulMeta) field.ErrorList { var errs field.ErrorList path := field.NewPath("spec") - if !namespacesEnabled { + if !consulMeta.NamespacesEnabled { if in.Spec.Redirect != nil { if in.Spec.Redirect.Namespace != "" { errs = append(errs, field.Invalid(path.Child("redirect").Child("namespace"), in.Spec.Redirect.Namespace, `Consul Enterprise namespaces must be enabled to set redirect.namespace`)) @@ -449,7 +452,13 @@ func (in *ServiceResolver) validateNamespaces(namespacesEnabled bool) field.Erro errs = append(errs, field.Invalid(path.Child("failover").Key(k).Child("namespace"), v.Namespace, `Consul Enterprise namespaces must be enabled to set failover.namespace`)) } } - + } + if !consulMeta.PartitionsEnabled { + if in.Spec.Redirect != nil { + if in.Spec.Redirect.Partition != "" { + errs = append(errs, field.Invalid(path.Child("redirect").Child("partition"), in.Spec.Redirect.Partition, `Consul Enterprise partitions must be enabled to set redirect.partition`)) + } + } } return errs } diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index 979c1c983c..44b838cc50 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -455,6 +455,7 @@ func TestServiceResolver_Validate(t *testing.T) { cases := map[string]struct { input *ServiceResolver namespacesEnabled bool + partitionsEnabled bool expectedErrMsgs []string }{ "namespaces enabled: valid": { @@ -476,6 +477,7 @@ func TestServiceResolver_Validate(t *testing.T) { }, }, namespacesEnabled: true, + partitionsEnabled: false, expectedErrMsgs: nil, }, "namespaces disabled: valid": { @@ -495,6 +497,50 @@ func TestServiceResolver_Validate(t *testing.T) { }, }, namespacesEnabled: false, + partitionsEnabled: false, + expectedErrMsgs: nil, + }, + "partitions enabled: valid": { + input: &ServiceResolver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Service: "bar", + Namespace: "namespace-a", + Partition: "other", + }, + Failover: map[string]ServiceResolverFailover{ + "failA": { + Service: "baz", + Namespace: "namespace-b", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: nil, + }, + "partitions disabled: valid": { + input: &ServiceResolver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Service: "bar", + }, + Failover: map[string]ServiceResolverFailover{ + "failA": { + Service: "baz", + }, + }, + }, + }, + namespacesEnabled: false, + partitionsEnabled: false, expectedErrMsgs: nil, }, "failover service, servicesubset, namespace, datacenters empty": { @@ -662,6 +708,24 @@ func TestServiceResolver_Validate(t *testing.T) { "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.redirect.namespace: Invalid value: \"namespace-a\": Consul Enterprise namespaces must be enabled to set redirect.namespace", }, }, + "partitions disabled: redirect partition specified": { + input: &ServiceResolver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceResolverSpec{ + Redirect: &ServiceResolverRedirect{ + Namespace: "namespace-a", + Partition: "other", + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{ + "serviceresolver.consul.hashicorp.com \"foo\" is invalid: spec.redirect.partition: Invalid value: \"other\": Consul Enterprise partitions must be enabled to set redirect.partition", + }, + }, "namespaces disabled: single failover namespace specified": { input: &ServiceResolver{ ObjectMeta: metav1.ObjectMeta{ @@ -705,7 +769,7 @@ func TestServiceResolver_Validate(t *testing.T) { } for name, testCase := range cases { t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled}) + err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled, PartitionsEnabled: testCase.partitionsEnabled}) if len(testCase.expectedErrMsgs) != 0 { require.Error(t, err) for _, s := range testCase.expectedErrMsgs { diff --git a/control-plane/api/v1alpha1/servicerouter_types.go b/control-plane/api/v1alpha1/servicerouter_types.go index 4aa42e1624..dbe9c15895 100644 --- a/control-plane/api/v1alpha1/servicerouter_types.go +++ b/control-plane/api/v1alpha1/servicerouter_types.go @@ -128,6 +128,9 @@ type ServiceRouteDestination struct { // Namespace is the Consul namespace to resolve the service from instead of // the current namespace. If empty the current namespace is assumed. Namespace string `json:"namespace,omitempty"` + // Partition is the Consul partition to resolve the service from instead of + // the current partition. If empty the current partition is assumed. + Partition string `json:"partition,omitempty"` // PrefixRewrite defines how to rewrite the HTTP request path before proxying // it to its final destination. // This requires that either match.http.pathPrefix or match.http.pathExact @@ -254,7 +257,7 @@ func (in *ServiceRouter) Validate(consulMeta common.ConsulMeta) error { errs = append(errs, r.validate(path.Child("routes").Index(i))...) } - errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) + errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { return apierrors.NewInvalid( @@ -328,6 +331,7 @@ func (in *ServiceRouteDestination) toConsul() *capi.ServiceRouteDestination { Service: in.Service, ServiceSubset: in.ServiceSubset, Namespace: in.Namespace, + Partition: in.Partition, PrefixRewrite: in.PrefixRewrite, RequestTimeout: in.RequestTimeout.Duration, NumRetries: in.NumRetries, @@ -338,10 +342,10 @@ func (in *ServiceRouteDestination) toConsul() *capi.ServiceRouteDestination { } } -func (in *ServiceRouter) validateNamespaces(namespacesEnabled bool) field.ErrorList { +func (in *ServiceRouter) validateEnterprise(consulMeta common.ConsulMeta) field.ErrorList { var errs field.ErrorList path := field.NewPath("spec") - if !namespacesEnabled { + if !consulMeta.NamespacesEnabled { for i, r := range in.Spec.Routes { if r.Destination != nil { if r.Destination.Namespace != "" { @@ -350,6 +354,15 @@ func (in *ServiceRouter) validateNamespaces(namespacesEnabled bool) field.ErrorL } } } + if !consulMeta.PartitionsEnabled { + for i, r := range in.Spec.Routes { + if r.Destination != nil { + if r.Destination.Partition != "" { + errs = append(errs, field.Invalid(path.Child("routes").Index(i).Child("destination").Child("partition"), r.Destination.Partition, `Consul Enterprise partitions must be enabled to set destination.partition`)) + } + } + } + } return errs } diff --git a/control-plane/api/v1alpha1/servicerouter_types_test.go b/control-plane/api/v1alpha1/servicerouter_types_test.go index 5d984ddf8f..eb0568db81 100644 --- a/control-plane/api/v1alpha1/servicerouter_types_test.go +++ b/control-plane/api/v1alpha1/servicerouter_types_test.go @@ -521,6 +521,7 @@ func TestServiceRouter_Validate(t *testing.T) { cases := map[string]struct { input *ServiceRouter namespacesEnabled bool + partitionsEnabled bool expectedErrMsgs []string }{ "namespaces enabled: valid": { @@ -570,6 +571,56 @@ func TestServiceRouter_Validate(t *testing.T) { namespacesEnabled: false, expectedErrMsgs: nil, }, + "partitions enabled: valid": { + input: &ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceRouterSpec{ + Routes: []ServiceRoute{ + { + Match: &ServiceRouteMatch{ + HTTP: &ServiceRouteHTTPMatch{ + PathPrefix: "/admin", + }, + }, + Destination: &ServiceRouteDestination{ + Service: "destA", + Namespace: "namespace-a", + Partition: "other", + }, + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: nil, + }, + "partitions disabled: valid": { + input: &ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceRouterSpec{ + Routes: []ServiceRoute{ + { + Match: &ServiceRouteMatch{ + HTTP: &ServiceRouteHTTPMatch{ + PathPrefix: "/admin", + }, + }, + Destination: &ServiceRouteDestination{ + Service: "destA", + }, + }, + }, + }, + }, + namespacesEnabled: false, + partitionsEnabled: false, + expectedErrMsgs: nil, + }, "http match queryParam": { input: &ServiceRouter{ ObjectMeta: metav1.ObjectMeta{ @@ -712,10 +763,61 @@ func TestServiceRouter_Validate(t *testing.T) { "spec.routes[1].destination.namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set destination.namespace", }, }, + "partitions disabled: single destination partition specified": { + input: &ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceRouterSpec{ + Routes: []ServiceRoute{ + { + Destination: &ServiceRouteDestination{ + Namespace: "namespace-a", + Partition: "partition-a", + }, + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{ + "servicerouter.consul.hashicorp.com \"foo\" is invalid: spec.routes[0].destination.partition: Invalid value: \"partition-a\": Consul Enterprise partitions must be enabled to set destination.partition", + }, + }, + "partitions disabled: multiple destination partitions specified": { + input: &ServiceRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceRouterSpec{ + Routes: []ServiceRoute{ + { + Destination: &ServiceRouteDestination{ + Namespace: "namespace-a", + Partition: "partition-a", + }, + }, + { + Destination: &ServiceRouteDestination{ + Namespace: "namespace-b", + Partition: "partition-b", + }, + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{ + "spec.routes[0].destination.partition: Invalid value: \"partition-a\": Consul Enterprise partitions must be enabled to set destination.partition", + "spec.routes[1].destination.partition: Invalid value: \"partition-b\": Consul Enterprise partitions must be enabled to set destination.partition", + }, + }, } for name, testCase := range cases { t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled}) + err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled, PartitionsEnabled: testCase.partitionsEnabled}) if len(testCase.expectedErrMsgs) != 0 { require.Error(t, err) for _, s := range testCase.expectedErrMsgs { diff --git a/control-plane/api/v1alpha1/servicesplitter_types.go b/control-plane/api/v1alpha1/servicesplitter_types.go index ea41ed6b1a..900cc1c545 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types.go +++ b/control-plane/api/v1alpha1/servicesplitter_types.go @@ -61,9 +61,12 @@ type ServiceSplit struct { // ServiceSubset is a named subset of the given service to resolve instead of one defined // as that service's DefaultSubset. If empty the default subset is used. ServiceSubset string `json:"serviceSubset,omitempty"` - // The namespace to resolve the service from instead of the current namespace. - // If empty the current namespace is assumed. + // Namespace is the Consul namespace to resolve the service from instead of + // the current namespace. If empty the current namespace is assumed. Namespace string `json:"namespace,omitempty"` + // Partition is the Consul partition to resolve the service from instead of + // the current partition. If empty the current partition is assumed. + Partition string `json:"partition,omitempty"` // Allow HTTP header manipulation to be configured. RequestHeaders *HTTPHeaderModifiers `json:"requestHeaders,omitempty"` ResponseHeaders *HTTPHeaderModifiers `json:"responseHeaders,omitempty"` @@ -168,7 +171,7 @@ func (in *ServiceSplitter) MatchesConsul(candidate capi.ConfigEntry) bool { func (in *ServiceSplitter) Validate(consulMeta common.ConsulMeta) error { errs := in.Spec.Splits.validate(field.NewPath("spec").Child("splits")) - errs = append(errs, in.validateNamespaces(consulMeta.NamespacesEnabled)...) + errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { return apierrors.NewInvalid( @@ -198,21 +201,29 @@ func (in ServiceSplit) toConsul() capi.ServiceSplit { Service: in.Service, ServiceSubset: in.ServiceSubset, Namespace: in.Namespace, + Partition: in.Partition, RequestHeaders: in.RequestHeaders.toConsul(), ResponseHeaders: in.ResponseHeaders.toConsul(), } } -func (in *ServiceSplitter) validateNamespaces(namespacesEnabled bool) field.ErrorList { +func (in *ServiceSplitter) validateEnterprise(consulMeta common.ConsulMeta) field.ErrorList { var errs field.ErrorList path := field.NewPath("spec") - if !namespacesEnabled { + if !consulMeta.NamespacesEnabled { for i, s := range in.Spec.Splits { if s.Namespace != "" { errs = append(errs, field.Invalid(path.Child("splits").Index(i).Child("namespace"), s.Namespace, `Consul Enterprise namespaces must be enabled to set split.namespace`)) } } } + if !consulMeta.PartitionsEnabled { + for i, s := range in.Spec.Splits { + if s.Partition != "" { + errs = append(errs, field.Invalid(path.Child("splits").Index(i).Child("partition"), s.Partition, `Consul Enterprise partitions must be enabled to set split.partition`)) + } + } + } return errs } diff --git a/control-plane/api/v1alpha1/servicesplitter_types_test.go b/control-plane/api/v1alpha1/servicesplitter_types_test.go index e37d29e9db..48e9eeac54 100644 --- a/control-plane/api/v1alpha1/servicesplitter_types_test.go +++ b/control-plane/api/v1alpha1/servicesplitter_types_test.go @@ -387,6 +387,7 @@ func TestServiceSplitter_Validate(t *testing.T) { cases := map[string]struct { input *ServiceSplitter namespacesEnabled bool + partitionsEnabled bool expectedErrMsgs []string }{ "namespaces enabled: valid": { @@ -429,6 +430,50 @@ func TestServiceSplitter_Validate(t *testing.T) { namespacesEnabled: false, expectedErrMsgs: nil, }, + "partitions enabled: valid": { + input: &ServiceSplitter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceSplitterSpec{ + Splits: []ServiceSplit{ + { + Weight: 99.99, + Namespace: "namespace-a", + Partition: "partition-a", + }, + { + Weight: 0.01, + Namespace: "namespace-b", + Partition: "partition-b", + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: true, + expectedErrMsgs: nil, + }, + "partitions disabled: valid": { + input: &ServiceSplitter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceSplitterSpec{ + Splits: []ServiceSplit{ + { + Weight: 99.99, + }, + { + Weight: 0.01, + }, + }, + }, + }, + namespacesEnabled: false, + partitionsEnabled: false, + expectedErrMsgs: nil, + }, "splits with 0 weight: valid": { input: &ServiceSplitter{ ObjectMeta: metav1.ObjectMeta{ @@ -541,10 +586,58 @@ func TestServiceSplitter_Validate(t *testing.T) { "spec.splits[1].namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set split.namespace", }, }, + "partitions disabled: single split partition specified": { + input: &ServiceSplitter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceSplitterSpec{ + Splits: []ServiceSplit{ + { + Namespace: "namespace-a", + Partition: "partition-a", + Weight: 100, + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{ + "servicesplitter.consul.hashicorp.com \"foo\" is invalid: spec.splits[0].partition: Invalid value: \"partition-a\": Consul Enterprise partitions must be enabled to set split.partition", + }, + }, + "partitions disabled: multiple split partitions specified": { + input: &ServiceSplitter{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: ServiceSplitterSpec{ + Splits: []ServiceSplit{ + { + Namespace: "namespace-a", + Partition: "partition-a", + Weight: 50, + }, + { + Namespace: "namespace-b", + Partition: "partition-b", + Weight: 50, + }, + }, + }, + }, + namespacesEnabled: true, + partitionsEnabled: false, + expectedErrMsgs: []string{ + "spec.splits[0].partition: Invalid value: \"partition-a\": Consul Enterprise partitions must be enabled to set split.partition", + "spec.splits[1].partition: Invalid value: \"partition-b\": Consul Enterprise partitions must be enabled to set split.partition", + }, + }, } for name, testCase := range cases { t.Run(name, func(t *testing.T) { - err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled}) + err := testCase.input.Validate(common.ConsulMeta{NamespacesEnabled: testCase.namespacesEnabled, PartitionsEnabled: testCase.partitionsEnabled}) if len(testCase.expectedErrMsgs) != 0 { require.Error(t, err) for _, s := range testCase.expectedErrMsgs { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index eb7b167895..2fc836ebe8 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -1,4 +1,5 @@ //go:build !ignore_autogenerated +// +build !ignore_autogenerated // Code generated by controller-gen. DO NOT EDIT. @@ -97,6 +98,87 @@ func (in *ExportedService) DeepCopy() *ExportedService { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExportedServices) DeepCopyInto(out *ExportedServices) { + *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 ExportedServices. +func (in *ExportedServices) DeepCopy() *ExportedServices { + if in == nil { + return nil + } + out := new(ExportedServices) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ExportedServices) 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 *ExportedServicesList) DeepCopyInto(out *ExportedServicesList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ExportedServices, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExportedServicesList. +func (in *ExportedServicesList) DeepCopy() *ExportedServicesList { + if in == nil { + return nil + } + out := new(ExportedServicesList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ExportedServicesList) 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 *ExportedServicesSpec) DeepCopyInto(out *ExportedServicesSpec) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]ExportedService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExportedServicesSpec. +func (in *ExportedServicesSpec) DeepCopy() *ExportedServicesSpec { + if in == nil { + return nil + } + out := new(ExportedServicesSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Expose) DeepCopyInto(out *Expose) { *out = *in @@ -641,87 +723,6 @@ func (in *MeshSpec) DeepCopy() *MeshSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PartitionExports) DeepCopyInto(out *PartitionExports) { - *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 PartitionExports. -func (in *PartitionExports) DeepCopy() *PartitionExports { - if in == nil { - return nil - } - out := new(PartitionExports) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PartitionExports) 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 *PartitionExportsList) DeepCopyInto(out *PartitionExportsList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PartitionExports, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PartitionExportsList. -func (in *PartitionExportsList) DeepCopy() *PartitionExportsList { - if in == nil { - return nil - } - out := new(PartitionExportsList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PartitionExportsList) 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 *PartitionExportsSpec) DeepCopyInto(out *PartitionExportsSpec) { - *out = *in - if in.Services != nil { - in, out := &in.Services, &out.Services - *out = make([]ExportedService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PartitionExportsSpec. -func (in *PartitionExportsSpec) DeepCopy() *PartitionExportsSpec { - if in == nil { - return nil - } - out := new(PartitionExportsSpec) - 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 diff --git a/control-plane/config/certmanager/certificate.yaml b/control-plane/config/certmanager/certificate.yaml deleted file mode 100644 index 58db114fa0..0000000000 --- a/control-plane/config/certmanager/certificate.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# The following manifests contain a self-signed issuer CR and a certificate CR. -# More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for -# breaking changes -apiVersion: cert-manager.io/v1alpha2 -kind: Issuer -metadata: - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- -apiVersion: cert-manager.io/v1alpha2 -kind: Certificate -metadata: - name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml - namespace: system -spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize - dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/control-plane/config/certmanager/kustomization.yaml b/control-plane/config/certmanager/kustomization.yaml deleted file mode 100644 index bebea5a595..0000000000 --- a/control-plane/config/certmanager/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: -- certificate.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/control-plane/config/certmanager/kustomizeconfig.yaml b/control-plane/config/certmanager/kustomizeconfig.yaml deleted file mode 100644 index 90d7c313ca..0000000000 --- a/control-plane/config/certmanager/kustomizeconfig.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This configuration is for teaching kustomize how to update name ref and var substitution -nameReference: -- kind: Issuer - group: cert-manager.io - fieldSpecs: - - kind: Certificate - group: cert-manager.io - path: spec/issuerRef/name - -varReference: -- kind: Certificate - group: cert-manager.io - path: spec/commonName -- kind: Certificate - group: cert-manager.io - path: spec/dnsNames diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_partitionexports.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml similarity index 93% rename from control-plane/config/crd/bases/consul.hashicorp.com_partitionexports.yaml rename to control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 8e31c6ade8..7c9b46192b 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_partitionexports.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -6,14 +6,14 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.0 creationTimestamp: null - name: partitionexports.consul.hashicorp.com + name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com names: - kind: PartitionExports - listKind: PartitionExportsList - plural: partitionexports - singular: partitionexports + kind: ExportedServices + listKind: ExportedServicesList + plural: exportedservices + singular: exportedservices scope: Namespaced versions: - additionalPrinterColumns: @@ -32,7 +32,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: PartitionExports is the Schema for the partitionexports API + description: ExportedServices is the Schema for the exportedservices API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -47,7 +47,7 @@ spec: metadata: type: object spec: - description: PartitionExportsSpec defines the desired state of PartitionExports + description: ExportedServicesSpec defines the desired state of ExportedServices properties: services: description: Services is a list of services to be exported and the 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 0be581e8b1..87285e8563 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -180,8 +180,14 @@ spec: from instead of the current one. type: string namespace: - description: Namespace is the namespace to resolve the service - from instead of the current one. + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. type: string service: description: Service is a service to resolve instead of the current 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 17c7c75790..ec74f9b63d 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -70,6 +70,11 @@ spec: the request when a retryable result occurs format: int32 type: integer + partition: + description: Partition is the Consul partition to resolve + the service from instead of the current partition. If + empty the current partition is assumed. + type: string prefixRewrite: description: PrefixRewrite defines how to rewrite the HTTP request path before proxying it to its final destination. 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 de3d2d43fd..fbbac1010e 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -56,9 +56,14 @@ spec: items: properties: namespace: - description: The namespace to resolve the service from instead - of the current namespace. If empty the current namespace is - assumed. + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. type: string requestHeaders: description: Allow HTTP header manipulation to be configured. diff --git a/control-plane/config/crd/kustomization.yaml b/control-plane/config/crd/kustomization.yaml deleted file mode 100644 index 1c9666aac6..0000000000 --- a/control-plane/config/crd/kustomization.yaml +++ /dev/null @@ -1,45 +0,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 -resources: -- bases/consul.hashicorp.com_servicedefaults.yaml -- bases/consul.hashicorp.com_serviceresolvers.yaml -- bases/consul.hashicorp.com_proxydefaults.yaml -- bases/consul.hashicorp.com_servicerouters.yaml -- bases/consul.hashicorp.com_serviceintentions.yaml -- bases/consul.hashicorp.com_ingressgateways.yaml -- bases/consul.hashicorp.com_terminatinggateways.yaml -- bases/consul.hashicorp.com_meshes.yaml -- bases/consul.hashicorp.com_partitionexports.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_servicedefaults.yaml -- patches/webhook_in_serviceresolvers.yaml -- patches/webhook_in_proxydefaults.yaml -- patches/webhook_in_servicerouters.yaml -- patches/webhook_in_serviceintentions.yaml -- patches/webhook_in_ingressgateways.yaml -- patches/webhook_in_terminatinggateways.yaml -#- patches/webhook_in_meshes.yaml -#- patches/webhook_in_partitionexports.yaml -# +kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_servicedefaults.yaml -#- patches/cainjection_in_serviceresolvers.yaml -#- patches/cainjection_in_proxydefaults.yaml -#- patches/cainjection_in_servicerouters.yaml -#- patches/cainjection_in_serviceintentions.yaml -#- patches/cainjection_in_ingressgateways.yaml -#- patches/cainjection_in_terminatinggateways.yaml -#- patches/cainjection_in_meshes.yaml -#- patches/cainjection_in_partitionexports.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 6f83d9a94b..0000000000 --- a/control-plane/config/crd/kustomizeconfig.yaml +++ /dev/null @@ -1,17 +0,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/crd/patches/cainjection_in_ingressgateways.yaml b/control-plane/config/crd/patches/cainjection_in_ingressgateways.yaml deleted file mode 100644 index aa1074fa2b..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_ingressgateways.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ingressgateways.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_proxydefaults.yaml b/control-plane/config/crd/patches/cainjection_in_proxydefaults.yaml deleted file mode 100644 index df0460c54c..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_proxydefaults.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: proxydefaults.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_servicedefaults.yaml b/control-plane/config/crd/patches/cainjection_in_servicedefaults.yaml deleted file mode 100644 index 3ae80de8ce..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_servicedefaults.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: servicedefaults.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_serviceintentions.yaml b/control-plane/config/crd/patches/cainjection_in_serviceintentions.yaml deleted file mode 100644 index e53864ab64..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_serviceintentions.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: serviceintentions.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_serviceresolvers.yaml b/control-plane/config/crd/patches/cainjection_in_serviceresolvers.yaml deleted file mode 100644 index 39a09eb8e1..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_serviceresolvers.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: serviceresolvers.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_servicerouters.yaml b/control-plane/config/crd/patches/cainjection_in_servicerouters.yaml deleted file mode 100644 index 798e76fd2a..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_servicerouters.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: servicerouters.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_servicesplitters.yaml b/control-plane/config/crd/patches/cainjection_in_servicesplitters.yaml deleted file mode 100644 index 288ca2cf23..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_servicesplitters.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: servicesplitters.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/cainjection_in_terminatinggateways.yaml b/control-plane/config/crd/patches/cainjection_in_terminatinggateways.yaml deleted file mode 100644 index 9423b79fbf..0000000000 --- a/control-plane/config/crd/patches/cainjection_in_terminatinggateways.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: terminatinggateways.consul.hashicorp.com diff --git a/control-plane/config/crd/patches/webhook_in_ingressgateways.yaml b/control-plane/config/crd/patches/webhook_in_ingressgateways.yaml deleted file mode 100644 index fcc6f69fa5..0000000000 --- a/control-plane/config/crd/patches/webhook_in_ingressgateways.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: ingressgateways.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_partitionexports.yaml b/control-plane/config/crd/patches/webhook_in_partitionexports.yaml deleted file mode 100644 index 3084d0409c..0000000000 --- a/control-plane/config/crd/patches/webhook_in_partitionexports.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: partitionexports.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_proxydefaults.yaml b/control-plane/config/crd/patches/webhook_in_proxydefaults.yaml deleted file mode 100644 index 4d2d4bfff1..0000000000 --- a/control-plane/config/crd/patches/webhook_in_proxydefaults.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: proxydefaults.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_servicedefaults.yaml b/control-plane/config/crd/patches/webhook_in_servicedefaults.yaml deleted file mode 100644 index 774953f50f..0000000000 --- a/control-plane/config/crd/patches/webhook_in_servicedefaults.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: servicedefaults.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_serviceintentions.yaml b/control-plane/config/crd/patches/webhook_in_serviceintentions.yaml deleted file mode 100644 index 77fcdb48ff..0000000000 --- a/control-plane/config/crd/patches/webhook_in_serviceintentions.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: serviceintentions.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_serviceresolvers.yaml b/control-plane/config/crd/patches/webhook_in_serviceresolvers.yaml deleted file mode 100644 index b9f9b42a21..0000000000 --- a/control-plane/config/crd/patches/webhook_in_serviceresolvers.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: serviceresolvers.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_servicerouters.yaml b/control-plane/config/crd/patches/webhook_in_servicerouters.yaml deleted file mode 100644 index e4856434c8..0000000000 --- a/control-plane/config/crd/patches/webhook_in_servicerouters.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: servicerouters.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_servicesplitters.yaml b/control-plane/config/crd/patches/webhook_in_servicesplitters.yaml deleted file mode 100644 index e8f7c1371f..0000000000 --- a/control-plane/config/crd/patches/webhook_in_servicesplitters.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: servicesplitters.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/crd/patches/webhook_in_terminatinggateways.yaml b/control-plane/config/crd/patches/webhook_in_terminatinggateways.yaml deleted file mode 100644 index 05063cc954..0000000000 --- a/control-plane/config/crd/patches/webhook_in_terminatinggateways.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: terminatinggateways.consul.hashicorp.com -spec: - conversion: - strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert diff --git a/control-plane/config/default/kustomization.yaml b/control-plane/config/default/kustomization.yaml deleted file mode 100644 index df978482e5..0000000000 --- a/control-plane/config/default/kustomization.yaml +++ /dev/null @@ -1,74 +0,0 @@ -# Adds namespace to all resources. -namespace: default - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: consul-controller- - -# Labels to add to all resources and selectors. -#commonLabels: -# someName: someValue - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - - # Protect the /metrics endpoint by putting it behind auth. - # If you want your controller-manager to expose the /metrics - # endpoint w/o any authn/z, please comment the following line. - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -patchesStrategicMerge: -- manager_auth_proxy_patch.yaml -- manager_webhook_patch.yaml -- webhookcainjection_patch.yaml - -# the following config is for teaching kustomize how to do var substitution -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -vars: -- fieldref: - fieldPath: metadata.namespace - name: CERTIFICATE_NAMESPACE - objref: - group: cert-manager.io - kind: Certificate - name: serving-cert - version: v1alpha2 -- fieldref: {} - name: CERTIFICATE_NAME - objref: - group: cert-manager.io - kind: Certificate - name: serving-cert - version: v1alpha2 -- fieldref: - fieldPath: metadata.namespace - name: SERVICE_NAMESPACE - objref: - kind: Service - name: webhook-service - version: v1 -- fieldref: {} - name: SERVICE_NAME - objref: - kind: Service - name: webhook-service - version: v1 -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: -- ../crd -- ../rbac -- ../manager -- ../webhook -- ../certmanager diff --git a/control-plane/config/default/manager_auth_proxy_patch.yaml b/control-plane/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 77e743d1c1..0000000000 --- a/control-plane/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=10" - ports: - - containerPort: 8443 - name: https - - name: manager - args: - - "--metrics-addr=127.0.0.1:8080" - - "--enable-leader-election" diff --git a/control-plane/config/default/manager_webhook_patch.yaml b/control-plane/config/default/manager_webhook_patch.yaml deleted file mode 100644 index 738de350b7..0000000000 --- a/control-plane/config/default/manager_webhook_patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert diff --git a/control-plane/config/default/webhookcainjection_patch.yaml b/control-plane/config/default/webhookcainjection_patch.yaml deleted file mode 100644 index 79c5b20a63..0000000000 --- a/control-plane/config/default/webhookcainjection_patch.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -metadata: - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/control-plane/config/manager/kustomization.yaml b/control-plane/config/manager/kustomization.yaml deleted file mode 100644 index 957b423a0a..0000000000 --- a/control-plane/config/manager/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -resources: -- manager.yaml -# todo: this was auto-generated -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: ashwinvenkatesh/consul-k8s - newTag: latest diff --git a/control-plane/config/manager/manager.yaml b/control-plane/config/manager/manager.yaml deleted file mode 100644 index b086533218..0000000000 --- a/control-plane/config/manager/manager.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - control-plane: controller-manager -spec: - selector: - matchLabels: - control-plane: controller-manager - replicas: 1 - template: - metadata: - labels: - control-plane: controller-manager - spec: - containers: - - name: manager - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: CONSUL_HTTP_ADDR - value: http://$(HOST_IP):8500 - command: - - consul-k8s - - controller - args: - - --enable-leader-election - image: controller:latest - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - - terminationGracePeriodSeconds: 10 diff --git a/control-plane/config/rbac/auth_proxy_client_clusterrole.yaml b/control-plane/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index 7d62534c5f..0000000000 --- a/control-plane/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: metrics-reader -rules: -- nonResourceURLs: ["/metrics"] - verbs: ["get"] diff --git a/control-plane/config/rbac/auth_proxy_role.yaml b/control-plane/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index 618f5e4177..0000000000 --- a/control-plane/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: proxy-role -rules: -- apiGroups: ["authentication.k8s.io"] - resources: - - tokenreviews - verbs: ["create"] -- apiGroups: ["authorization.k8s.io"] - resources: - - subjectaccessreviews - verbs: ["create"] diff --git a/control-plane/config/rbac/auth_proxy_role_binding.yaml b/control-plane/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index 48ed1e4b85..0000000000 --- a/control-plane/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/control-plane/config/rbac/auth_proxy_service.yaml b/control-plane/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index 6cf656be14..0000000000 --- a/control-plane/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - name: controller-manager-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - targetPort: https - selector: - control-plane: controller-manager diff --git a/control-plane/config/rbac/ingressgateway_editor_role.yaml b/control-plane/config/rbac/ingressgateway_editor_role.yaml deleted file mode 100644 index 424e12e33c..0000000000 --- a/control-plane/config/rbac/ingressgateway_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit ingressgateways. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: ingressgateway-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - ingressgateways - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - ingressgateways/status - verbs: - - get diff --git a/control-plane/config/rbac/ingressgateway_viewer_role.yaml b/control-plane/config/rbac/ingressgateway_viewer_role.yaml deleted file mode 100644 index 82ca5e79db..0000000000 --- a/control-plane/config/rbac/ingressgateway_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view ingressgateways. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: ingressgateway-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - ingressgateways - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - ingressgateways/status - verbs: - - get diff --git a/control-plane/config/rbac/kustomization.yaml b/control-plane/config/rbac/kustomization.yaml deleted file mode 100644 index 66c28338fe..0000000000 --- a/control-plane/config/rbac/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -resources: -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml diff --git a/control-plane/config/rbac/leader_election_role.yaml b/control-plane/config/rbac/leader_election_role.yaml deleted file mode 100644 index 7dc16c420e..0000000000 --- a/control-plane/config/rbac/leader_election_role.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# permissions to do leader election. -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: leader-election-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch diff --git a/control-plane/config/rbac/leader_election_role_binding.yaml b/control-plane/config/rbac/leader_election_role_binding.yaml deleted file mode 100644 index eed16906f4..0000000000 --- a/control-plane/config/rbac/leader_election_role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: leader-election-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: leader-election-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/control-plane/config/rbac/partitionexport_editor_role.yaml b/control-plane/config/rbac/partitionexport_editor_role.yaml deleted file mode 100644 index 45b9d4c5c0..0000000000 --- a/control-plane/config/rbac/partitionexport_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit partitionexports. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: partitionexport-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - partitionexports - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - partitionexports/status - verbs: - - get diff --git a/control-plane/config/rbac/partitionexport_viewer_role.yaml b/control-plane/config/rbac/partitionexport_viewer_role.yaml deleted file mode 100644 index ce62786b98..0000000000 --- a/control-plane/config/rbac/partitionexport_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view partitionexports. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: partitionexport-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - partitionexports - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - partitionexports/status - verbs: - - get diff --git a/control-plane/config/rbac/proxydefaults_editor_role.yaml b/control-plane/config/rbac/proxydefaults_editor_role.yaml deleted file mode 100644 index 42afc1a916..0000000000 --- a/control-plane/config/rbac/proxydefaults_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit proxydefaults. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: proxydefaults-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - proxydefaults - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - proxydefaults/status - verbs: - - get diff --git a/control-plane/config/rbac/proxydefaults_viewer_role.yaml b/control-plane/config/rbac/proxydefaults_viewer_role.yaml deleted file mode 100644 index b16fda3894..0000000000 --- a/control-plane/config/rbac/proxydefaults_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view proxydefaults. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: proxydefaults-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - proxydefaults - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - proxydefaults/status - verbs: - - get diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index f1a46e72d3..6009210f4b 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -9,7 +9,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - ingressgateways + - exportedservices verbs: - create - delete @@ -21,7 +21,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - ingressgateways/status + - exportedservices/status verbs: - get - patch @@ -29,7 +29,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - mesh + - ingressgateways verbs: - create - delete @@ -41,7 +41,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - mesh/status + - ingressgateways/status verbs: - get - patch @@ -49,7 +49,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - partitionexports + - mesh verbs: - create - delete @@ -61,7 +61,7 @@ rules: - apiGroups: - consul.hashicorp.com resources: - - partitionexports/status + - mesh/status verbs: - get - patch diff --git a/control-plane/config/rbac/role_binding.yaml b/control-plane/config/rbac/role_binding.yaml deleted file mode 100644 index 8f2658702c..0000000000 --- a/control-plane/config/rbac/role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: system diff --git a/control-plane/config/rbac/servicedefaults_editor_role.yaml b/control-plane/config/rbac/servicedefaults_editor_role.yaml deleted file mode 100644 index f5a7d27a26..0000000000 --- a/control-plane/config/rbac/servicedefaults_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit servicedefaults. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicedefaults-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicedefaults - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicedefaults/status - verbs: - - get diff --git a/control-plane/config/rbac/servicedefaults_viewer_role.yaml b/control-plane/config/rbac/servicedefaults_viewer_role.yaml deleted file mode 100644 index ac9ccc9b3b..0000000000 --- a/control-plane/config/rbac/servicedefaults_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view servicedefaults. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicedefaults-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicedefaults - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicedefaults/status - verbs: - - get diff --git a/control-plane/config/rbac/serviceintentions_editor_role.yaml b/control-plane/config/rbac/serviceintentions_editor_role.yaml deleted file mode 100644 index 83a0437d7d..0000000000 --- a/control-plane/config/rbac/serviceintentions_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit serviceintentions. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceintentions-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceintentions - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceintentions/status - verbs: - - get diff --git a/control-plane/config/rbac/serviceintentions_viewer_role.yaml b/control-plane/config/rbac/serviceintentions_viewer_role.yaml deleted file mode 100644 index 6a5f41c960..0000000000 --- a/control-plane/config/rbac/serviceintentions_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view serviceintentions. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceintentions-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceintentions - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceintentions/status - verbs: - - get diff --git a/control-plane/config/rbac/serviceresolver_editor_role.yaml b/control-plane/config/rbac/serviceresolver_editor_role.yaml deleted file mode 100644 index 5baff84934..0000000000 --- a/control-plane/config/rbac/serviceresolver_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit serviceresolvers. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceresolver-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceresolvers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceresolvers/status - verbs: - - get diff --git a/control-plane/config/rbac/serviceresolver_viewer_role.yaml b/control-plane/config/rbac/serviceresolver_viewer_role.yaml deleted file mode 100644 index ca990258fb..0000000000 --- a/control-plane/config/rbac/serviceresolver_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view serviceresolvers. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: serviceresolver-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - serviceresolvers - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - serviceresolvers/status - verbs: - - get diff --git a/control-plane/config/rbac/servicerouter_editor_role.yaml b/control-plane/config/rbac/servicerouter_editor_role.yaml deleted file mode 100644 index c66e6c1ddd..0000000000 --- a/control-plane/config/rbac/servicerouter_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit servicerouters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicerouter-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicerouters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicerouters/status - verbs: - - get diff --git a/control-plane/config/rbac/servicerouter_viewer_role.yaml b/control-plane/config/rbac/servicerouter_viewer_role.yaml deleted file mode 100644 index c2cb68dfe8..0000000000 --- a/control-plane/config/rbac/servicerouter_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view servicerouters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicerouter-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicerouters - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicerouters/status - verbs: - - get diff --git a/control-plane/config/rbac/servicesplitter_editor_role.yaml b/control-plane/config/rbac/servicesplitter_editor_role.yaml deleted file mode 100644 index ec08f8a114..0000000000 --- a/control-plane/config/rbac/servicesplitter_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit servicesplitters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicesplitter-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicesplitters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicesplitters/status - verbs: - - get diff --git a/control-plane/config/rbac/servicesplitter_viewer_role.yaml b/control-plane/config/rbac/servicesplitter_viewer_role.yaml deleted file mode 100644 index 6e9458243f..0000000000 --- a/control-plane/config/rbac/servicesplitter_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view servicesplitters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: servicesplitter-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - servicesplitters - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - servicesplitters/status - verbs: - - get diff --git a/control-plane/config/rbac/terminatinggateway_editor_role.yaml b/control-plane/config/rbac/terminatinggateway_editor_role.yaml deleted file mode 100644 index 6d5da0b881..0000000000 --- a/control-plane/config/rbac/terminatinggateway_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit terminatinggateways. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: terminatinggateway-editor-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - terminatinggateways - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - terminatinggateways/status - verbs: - - get diff --git a/control-plane/config/rbac/terminatinggateway_viewer_role.yaml b/control-plane/config/rbac/terminatinggateway_viewer_role.yaml deleted file mode 100644 index 6f6c220d59..0000000000 --- a/control-plane/config/rbac/terminatinggateway_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view terminatinggateways. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: terminatinggateway-viewer-role -rules: -- apiGroups: - - consul.hashicorp.com - resources: - - terminatinggateways - verbs: - - get - - list - - watch -- apiGroups: - - consul.hashicorp.com - resources: - - terminatinggateways/status - verbs: - - get diff --git a/control-plane/config/samples/consul_v1alpha1_ingressgateway.yaml b/control-plane/config/samples/consul_v1alpha1_ingressgateway.yaml deleted file mode 100644 index 9a87883ed0..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_ingressgateway.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: IngressGateway -metadata: - name: ingressgateway-sample -spec: - tls: - enabled: false - listeners: - - port: 8080 - protocol: "tcp" - services: - - name: "foo" diff --git a/control-plane/config/samples/consul_v1alpha1_partitionexport.yaml b/control-plane/config/samples/consul_v1alpha1_partitionexport.yaml deleted file mode 100644 index fa34ecf043..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_partitionexport.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: PartitionExport -metadata: - name: exports -spec: - # Add fields here - foo: bar diff --git a/control-plane/config/samples/consul_v1alpha1_proxydefaults.yaml b/control-plane/config/samples/consul_v1alpha1_proxydefaults.yaml deleted file mode 100644 index 2a87f89ed7..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_proxydefaults.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ProxyDefaults -metadata: - name: proxydefaults-sample -spec: - # Add fields here - foo: bar diff --git a/control-plane/config/samples/consul_v1alpha1_servicedefaults.yaml b/control-plane/config/samples/consul_v1alpha1_servicedefaults.yaml deleted file mode 100644 index f395256b81..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_servicedefaults.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceDefaults -metadata: - name: servicedefaults-sample -spec: - protocol: "http" diff --git a/control-plane/config/samples/consul_v1alpha1_serviceintentions.yaml b/control-plane/config/samples/consul_v1alpha1_serviceintentions.yaml deleted file mode 100644 index 1c6dc83597..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_serviceintentions.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceIntentions -metadata: - name: serviceintentions-sample -spec: - # Add fields here - foo: bar diff --git a/control-plane/config/samples/consul_v1alpha1_serviceresolver.yaml b/control-plane/config/samples/consul_v1alpha1_serviceresolver.yaml deleted file mode 100644 index f1a2f29c8b..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_serviceresolver.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceResolver -metadata: - name: serviceresolver-sample -spec: - # Add fields here - foo: bar diff --git a/control-plane/config/samples/consul_v1alpha1_servicerouter.yaml b/control-plane/config/samples/consul_v1alpha1_servicerouter.yaml deleted file mode 100644 index df597031a9..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_servicerouter.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceRouter -metadata: - name: servicerouter-sample -spec: - routes: - - match: - http: - pathPrefix: "/admin" - destination: - service: admin diff --git a/control-plane/config/samples/consul_v1alpha1_servicesplitter.yaml b/control-plane/config/samples/consul_v1alpha1_servicesplitter.yaml deleted file mode 100644 index a432bd8117..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_servicesplitter.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: ServiceSplitter -metadata: - name: servicesplitter-sample -spec: - # Add fields here - foo: bar diff --git a/control-plane/config/samples/consul_v1alpha1_terminatinggateway.yaml b/control-plane/config/samples/consul_v1alpha1_terminatinggateway.yaml deleted file mode 100644 index 4708f6cb8a..0000000000 --- a/control-plane/config/samples/consul_v1alpha1_terminatinggateway.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: consul.hashicorp.com/v1alpha1 -kind: TerminatingGateway -metadata: - name: terminatinggateway-sample -spec: - services: - - name: name - caFile: "caFile" - certFile: "certFile" - keyFile: "keyFile" - sni: "sni" diff --git a/control-plane/config/samples/kustomization.yaml b/control-plane/config/samples/kustomization.yaml deleted file mode 100644 index c6cf924560..0000000000 --- a/control-plane/config/samples/kustomization.yaml +++ /dev/null @@ -1,3 +0,0 @@ -## This file is auto-generated, do not modify ## -resources: -- consul_v1alpha1_servicedefaults.yaml diff --git a/control-plane/config/webhook/kustomization.yaml b/control-plane/config/webhook/kustomization.yaml deleted file mode 100644 index 9cf26134e4..0000000000 --- a/control-plane/config/webhook/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- manifests.yaml -- service.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/control-plane/config/webhook/kustomizeconfig.yaml b/control-plane/config/webhook/kustomizeconfig.yaml deleted file mode 100644 index 25e21e3c96..0000000000 --- a/control-plane/config/webhook/kustomizeconfig.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# the following config is for teaching kustomize where to look at when substituting vars. -# It requires kustomize v2.1.0 or newer to work properly. -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - - kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - -namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true -- kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - -varReference: -- path: metadata/annotations diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 8264b31887..ae0eb15fc5 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -13,9 +13,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-v1alpha1-ingressgateway + path: /mutate-v1alpha1-exportedservices failurePolicy: Fail - name: mutate-ingressgateway.consul.hashicorp.com + name: mutate-exportedservices.consul.hashicorp.com rules: - apiGroups: - consul.hashicorp.com @@ -25,7 +25,7 @@ webhooks: - CREATE - UPDATE resources: - - ingressgateways + - exportedservices sideEffects: None - admissionReviewVersions: - v1beta1 @@ -34,9 +34,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-v1alpha1-mesh + path: /mutate-v1alpha1-ingressgateway failurePolicy: Fail - name: mutate-mesh.consul.hashicorp.com + name: mutate-ingressgateway.consul.hashicorp.com rules: - apiGroups: - consul.hashicorp.com @@ -46,7 +46,7 @@ webhooks: - CREATE - UPDATE resources: - - mesh + - ingressgateways sideEffects: None - admissionReviewVersions: - v1beta1 @@ -55,9 +55,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-v1alpha1-partitionexports + path: /mutate-v1alpha1-mesh failurePolicy: Fail - name: mutate-partitionexports.consul.hashicorp.com + name: mutate-mesh.consul.hashicorp.com rules: - apiGroups: - consul.hashicorp.com @@ -67,7 +67,7 @@ webhooks: - CREATE - UPDATE resources: - - partitionexports + - mesh sideEffects: None - admissionReviewVersions: - v1beta1 diff --git a/control-plane/config/webhook/service.yaml b/control-plane/config/webhook/service.yaml deleted file mode 100644 index 31e0f82959..0000000000 --- a/control-plane/config/webhook/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: webhook-service - namespace: system -spec: - ports: - - port: 443 - targetPort: 9443 - selector: - control-plane: controller-manager diff --git a/control-plane/controller/partitionexports_controller.go b/control-plane/controller/exportedservices_controller.go similarity index 52% rename from control-plane/controller/partitionexports_controller.go rename to control-plane/controller/exportedservices_controller.go index fd1034a81b..be0445f4a1 100644 --- a/control-plane/controller/partitionexports_controller.go +++ b/control-plane/controller/exportedservices_controller.go @@ -12,29 +12,29 @@ import ( consulv1alpha1 "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" ) -// PartitionExportsController reconciles a PartitionExports object -type PartitionExportsController struct { +// ExportedServicesController reconciles a ExportedServices object +type ExportedServicesController struct { client.Client Log logr.Logger Scheme *runtime.Scheme ConfigEntryController *ConfigEntryController } -// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=partitionexports,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=partitionexports/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=exportedservices,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=consul.hashicorp.com,resources=exportedservices/status,verbs=get;update;patch -func (r *PartitionExportsController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.PartitionExports{}) +func (r *ExportedServicesController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.ConfigEntryController.ReconcileEntry(ctx, r, req, &consulv1alpha1.ExportedServices{}) } -func (r *PartitionExportsController) Logger(name types.NamespacedName) logr.Logger { +func (r *ExportedServicesController) Logger(name types.NamespacedName) logr.Logger { return r.Log.WithValues("request", name) } -func (r *PartitionExportsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ExportedServicesController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { return r.Status().Update(ctx, obj, opts...) } -func (r *PartitionExportsController) SetupWithManager(mgr ctrl.Manager) error { - return setupWithManager(mgr, &consulv1alpha1.PartitionExports{}, r) +func (r *ExportedServicesController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &consulv1alpha1.ExportedServices{}, r) } diff --git a/control-plane/controller/partitionexports_controller_ent_test.go b/control-plane/controller/exportedservices_controller_ent_test.go similarity index 81% rename from control-plane/controller/partitionexports_controller_ent_test.go rename to control-plane/controller/exportedservices_controller_ent_test.go index 02ed8bf31a..ec8f771586 100644 --- a/control-plane/controller/partitionexports_controller_ent_test.go +++ b/control-plane/controller/exportedservices_controller_ent_test.go @@ -23,13 +23,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -// This tests explicitly tests PartitionExportsController instead of using the existing +// This tests explicitly tests ExportedServicesController instead of using the existing // pattern of adding tests for the controller to configentry_controller test. That is because -// unlike the other CRDs, PartitionExports are only supported in Consul Enterprise. But the +// unlike the other CRDs, ExportedServices are only supported in Consul Enterprise. But the // test pattern of the enterprise tests already covers a config-entry similar to partition-exports // ie a "global" configentry. Hence a separate file has been created to test this controller. -func TestPartitionExportsController_createsPartitionExports(tt *testing.T) { +func TestExportedServicesController_createsExportedServices(tt *testing.T) { tt.Parallel() cases := map[string]struct { @@ -73,12 +73,12 @@ func TestPartitionExportsController_createsPartitionExports(tt *testing.T) { tt.Run(name, func(t *testing.T) { req := require.New(t) s := runtime.NewScheme() - partitionExport := &v1alpha1.PartitionExports{ + exportedServices := &v1alpha1.ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, }, - Spec: v1alpha1.PartitionExportsSpec{ + Spec: v1alpha1.ExportedServicesSpec{ Services: []v1alpha1.ExportedService{ { Name: "frontend", @@ -91,7 +91,7 @@ func TestPartitionExportsController_createsPartitionExports(tt *testing.T) { }, }, } - s.AddKnownTypes(v1alpha1.GroupVersion, partitionExport) + s.AddKnownTypes(v1alpha1.GroupVersion, exportedServices) ctx := context.Background() consul, err := testutil.NewTestServerConfigT(t, nil) @@ -103,9 +103,9 @@ func TestPartitionExportsController_createsPartitionExports(tt *testing.T) { }) req.NoError(err) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(partitionExport).Build() + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.PartitionExportsController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, @@ -121,34 +121,34 @@ func TestPartitionExportsController_createsPartitionExports(tt *testing.T) { resp, err := controller.Reconcile(ctx, ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: c.SourceKubeNS, - Name: partitionExport.KubernetesName(), + Name: exportedServices.KubernetesName(), }, }) req.NoError(err) req.False(resp.Requeue) - cfg, _, err := consulClient.ConfigEntries().Get(capi.PartitionExports, partitionExport.ConsulName(), &capi.QueryOptions{ + cfg, _, err := consulClient.ConfigEntries().Get(capi.ExportedServices, exportedServices.ConsulName(), &capi.QueryOptions{ Namespace: common.DefaultConsulNamespace, }) req.NoError(err) - configEntry, ok := cfg.(*capi.PartitionExportsConfigEntry) + configEntry, ok := cfg.(*capi.ExportedServicesConfigEntry) req.True(ok) req.Equal(configEntry.Services[0].Name, "frontend") // Check that the status is "synced". err = fakeClient.Get(ctx, types.NamespacedName{ Namespace: c.SourceKubeNS, - Name: partitionExport.KubernetesName(), - }, partitionExport) + Name: exportedServices.KubernetesName(), + }, exportedServices) req.NoError(err) - conditionSynced := partitionExport.SyncedConditionStatus() + conditionSynced := exportedServices.SyncedConditionStatus() req.Equal(conditionSynced, corev1.ConditionTrue) }) } } -func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { +func TestExportedServicesController_updatesExportedServices(tt *testing.T) { tt.Parallel() cases := map[string]struct { @@ -192,13 +192,13 @@ func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { tt.Run(name, func(t *testing.T) { req := require.New(t) s := runtime.NewScheme() - partitionExport := &v1alpha1.PartitionExports{ + exportedServices := &v1alpha1.ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, Finalizers: []string{controller.FinalizerName}, }, - Spec: v1alpha1.PartitionExportsSpec{ + Spec: v1alpha1.ExportedServicesSpec{ Services: []v1alpha1.ExportedService{ { Name: "frontend", @@ -211,7 +211,7 @@ func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { }, }, } - s.AddKnownTypes(v1alpha1.GroupVersion, partitionExport) + s.AddKnownTypes(v1alpha1.GroupVersion, exportedServices) ctx := context.Background() consul, err := testutil.NewTestServerConfigT(t, nil) @@ -223,9 +223,9 @@ func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { }) req.NoError(err) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(partitionExport).Build() + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.PartitionExportsController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, @@ -240,7 +240,7 @@ func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { // We haven't run reconcile yet so ensure it's created in Consul. { - _, _, err := consulClient.ConfigEntries().Set(&capi.PartitionExportsConfigEntry{ + _, _, err := consulClient.ConfigEntries().Set(&capi.ExportedServicesConfigEntry{ Name: "default", Services: []capi.ExportedService{ { @@ -261,36 +261,36 @@ func TestPartitionExportsController_updatesPartitionExports(tt *testing.T) { // First get it so we have the latest revision number. err = fakeClient.Get(ctx, types.NamespacedName{ Namespace: c.SourceKubeNS, - Name: partitionExport.KubernetesName(), - }, partitionExport) + Name: exportedServices.KubernetesName(), + }, exportedServices) req.NoError(err) // Update the resource. - partitionExport.Spec.Services[0].Name = "backend" - err := fakeClient.Update(ctx, partitionExport) + exportedServices.Spec.Services[0].Name = "backend" + err := fakeClient.Update(ctx, exportedServices) req.NoError(err) resp, err := controller.Reconcile(ctx, ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: c.SourceKubeNS, - Name: partitionExport.KubernetesName(), + Name: exportedServices.KubernetesName(), }, }) req.NoError(err) req.False(resp.Requeue) - cfg, _, err := consulClient.ConfigEntries().Get(capi.PartitionExports, partitionExport.ConsulName(), &capi.QueryOptions{ + cfg, _, err := consulClient.ConfigEntries().Get(capi.ExportedServices, exportedServices.ConsulName(), &capi.QueryOptions{ Namespace: common.DefaultConsulNamespace, }) req.NoError(err) - entry := cfg.(*capi.PartitionExportsConfigEntry) + entry := cfg.(*capi.ExportedServicesConfigEntry) req.Equal("backend", entry.Services[0].Name) } }) } } -func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { +func TestExportedServicesController_deletesExportedServices(tt *testing.T) { tt.Parallel() cases := map[string]struct { @@ -334,14 +334,14 @@ func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { tt.Run(name, func(t *testing.T) { req := require.New(t) s := runtime.NewScheme() - partitionExport := &v1alpha1.PartitionExports{ + exportedServices := &v1alpha1.ExportedServices{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: c.SourceKubeNS, Finalizers: []string{controller.FinalizerName}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, - Spec: v1alpha1.PartitionExportsSpec{ + Spec: v1alpha1.ExportedServicesSpec{ Services: []v1alpha1.ExportedService{ { Name: "frontend", @@ -354,7 +354,7 @@ func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { }, }, } - s.AddKnownTypes(v1alpha1.GroupVersion, partitionExport) + s.AddKnownTypes(v1alpha1.GroupVersion, exportedServices) consul, err := testutil.NewTestServerConfigT(t, nil) req.NoError(err) @@ -365,9 +365,9 @@ func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { }) req.NoError(err) - fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(partitionExport).Build() + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(exportedServices).Build() - controller := &controller.PartitionExportsController{ + controller := &controller.ExportedServicesController{ Client: fakeClient, Log: logrtest.TestLogger{T: t}, Scheme: s, @@ -382,7 +382,7 @@ func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { // We haven't run reconcile yet so ensure it's created in Consul. { - _, _, err := consulClient.ConfigEntries().Set(&capi.PartitionExportsConfigEntry{ + _, _, err := consulClient.ConfigEntries().Set(&capi.ExportedServicesConfigEntry{ Name: "default", Services: []capi.ExportedService{ { @@ -404,16 +404,16 @@ func TestPartitionExportsController_deletesPartitionExports(tt *testing.T) { resp, err := controller.Reconcile(context.Background(), ctrl.Request{ NamespacedName: types.NamespacedName{ Namespace: c.SourceKubeNS, - Name: partitionExport.KubernetesName(), + Name: exportedServices.KubernetesName(), }, }) req.NoError(err) req.False(resp.Requeue) - _, _, err = consulClient.ConfigEntries().Get(capi.PartitionExports, partitionExport.ConsulName(), &capi.QueryOptions{ + _, _, err = consulClient.ConfigEntries().Get(capi.ExportedServices, exportedServices.ConsulName(), &capi.QueryOptions{ Namespace: common.DefaultConsulNamespace, }) - req.EqualError(err, fmt.Sprintf(`Unexpected response code: 404 (Config entry not found for "%s" / "%s")`, capi.PartitionExports, partitionExport.ConsulName())) + req.EqualError(err, fmt.Sprintf(`Unexpected response code: 404 (Config entry not found for "%s" / "%s")`, capi.ExportedServices, exportedServices.ConsulName())) } }) } diff --git a/control-plane/go.mod b/control-plane/go.mod index 6f1d560066..2e1a81424b 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -6,7 +6,7 @@ require ( github.com/go-logr/logr v0.4.0 github.com/google/go-cmp v0.5.6 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/hashicorp/consul/api v1.10.1-0.20211116182834-e6956893fb6f + github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc github.com/hashicorp/consul/sdk v0.8.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f github.com/hashicorp/go-hclog v0.16.1 diff --git a/control-plane/go.sum b/control-plane/go.sum index 179772df60..6877dd034c 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -297,8 +297,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf 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/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20211116182834-e6956893fb6f h1:fBBh4412td7nBzqyLkpGTH5dWycPs8p7Yg/Dy8VQjVU= -github.com/hashicorp/consul/api v1.10.1-0.20211116182834-e6956893fb6f/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc h1:tUgL1cinAFDtidyKqgsJzlxLkEi9atLmN6j8kgCr17Q= +github.com/hashicorp/consul/api v1.10.1-0.20211206193229-9b44861ce4bc/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= diff --git a/control-plane/subcommand/consul-sidecar/command.go b/control-plane/subcommand/consul-sidecar/command.go index dcb05199ba..b6ff0d9d39 100644 --- a/control-plane/subcommand/consul-sidecar/command.go +++ b/control-plane/subcommand/consul-sidecar/command.go @@ -21,8 +21,13 @@ import ( "github.com/mitchellh/cli" ) -const metricsServerShutdownTimeout = 5 * time.Second -const envoyMetricsAddr = "http://127.0.0.1:19000/stats/prometheus" +const ( + metricsServerShutdownTimeout = 5 * time.Second + envoyMetricsAddr = "http://127.0.0.1:19000/stats/prometheus" + // prometheusServiceMetricsSuccessKey is the key of the prometheus metric used to + // indicate if service metrics were scraped successfully. + prometheusServiceMetricsSuccessKey = "consul_merged_service_metrics_success" +) type Command struct { UI cli.Ui @@ -240,27 +245,36 @@ func (c *Command) createMergedMetricsServer() *http.Server { mergedMetricsServerAddr := fmt.Sprintf("127.0.0.1:%s", c.flagMergedMetricsPort) server := &http.Server{Addr: mergedMetricsServerAddr, Handler: mux} + // http.Client satisfies the metricsGetter interface. // The default http.Client timeout is indefinite, so adding a timeout makes // sure that requests don't hang. client := &http.Client{ Timeout: time.Second * 10, } - // http.Client satisfies the metricsGetter interface. - c.envoyMetricsGetter = client - c.serviceMetricsGetter = client + + // During tests these may already be set to mocks. + if c.envoyMetricsGetter == nil { + c.envoyMetricsGetter = client + } + if c.serviceMetricsGetter == nil { + c.serviceMetricsGetter = client + } return server } // mergedMetricsHandler has the logic to append both Envoy and service metrics // together, logging if it's unsuccessful at either. +// If the Envoy scrape fails, we respond with a 500 code which follows the Prometheus +// exporter guidelines. If the service scrape fails, we respond with a 200 so +// that the Envoy metrics are still scraped. +// We also include a metric line in each response indicating the success or +// failure of the service metric scraping. func (c *Command) mergedMetricsHandler(rw http.ResponseWriter, _ *http.Request) { - envoyMetrics, err := c.envoyMetricsGetter.Get(envoyMetricsAddr) if err != nil { - // If there is an error scraping Envoy, we want the handler to return - // without writing anything to the response, and log the error. - c.logger.Error(fmt.Sprintf("Error scraping Envoy proxy metrics: %s", err.Error())) + c.logger.Error("Error scraping Envoy proxy metrics", "err", err) + http.Error(rw, fmt.Sprintf("Error scraping Envoy proxy metrics: %s", err), http.StatusInternalServerError) return } @@ -273,18 +287,22 @@ func (c *Command) mergedMetricsHandler(rw http.ResponseWriter, _ *http.Request) }() envoyMetricsBody, err := ioutil.ReadAll(envoyMetrics.Body) if err != nil { - c.logger.Error(fmt.Sprintf("Couldn't read Envoy proxy metrics: %s", err.Error())) + c.logger.Error("Could not read Envoy proxy metrics", "err", err) + http.Error(rw, fmt.Sprintf("Could not read Envoy proxy metrics: %s", err), http.StatusInternalServerError) return } - _, err = rw.Write(envoyMetricsBody) - if err != nil { - c.logger.Error(fmt.Sprintf("Error writing envoy metrics body: %s", err.Error())) + if non2xxCode(envoyMetrics.StatusCode) { + c.logger.Error("Received non-2xx status code scraping Envoy proxy metrics", "code", envoyMetrics.StatusCode, "response", string(envoyMetricsBody)) + http.Error(rw, fmt.Sprintf("Received non-2xx status code scraping Envoy proxy metrics: %d: %s", envoyMetrics.StatusCode, string(envoyMetricsBody)), http.StatusInternalServerError) + return } + writeResponse(rw, envoyMetricsBody, "envoy metrics", c.logger) serviceMetricsAddr := fmt.Sprintf("http://127.0.0.1:%s%s", c.flagServiceMetricsPort, c.flagServiceMetricsPath) serviceMetrics, err := c.serviceMetricsGetter.Get(serviceMetricsAddr) if err != nil { - c.logger.Warn(fmt.Sprintf("Error scraping service metrics: %s", err.Error())) + c.logger.Warn("Error scraping service metrics", "err", err) + writeResponse(rw, serviceMetricSuccess(false), "service metrics success", c.logger) // Since we've already written the Envoy metrics to the response, we can // return at this point if we were unable to get service metrics. return @@ -300,12 +318,25 @@ func (c *Command) mergedMetricsHandler(rw http.ResponseWriter, _ *http.Request) }() serviceMetricsBody, err := ioutil.ReadAll(serviceMetrics.Body) if err != nil { - c.logger.Error(fmt.Sprintf("Couldn't read service metrics: %s", err.Error())) + c.logger.Error("Could not read service metrics", "err", err) + writeResponse(rw, serviceMetricSuccess(false), "service metrics success", c.logger) return } - _, err = rw.Write(serviceMetricsBody) + if non2xxCode(serviceMetrics.StatusCode) { + c.logger.Error("Received non-2xx status code scraping service metrics", "code", serviceMetrics.StatusCode, "response", string(serviceMetricsBody)) + writeResponse(rw, serviceMetricSuccess(false), "service metrics success", c.logger) + return + } + writeResponse(rw, serviceMetricsBody, "service metrics", c.logger) + writeResponse(rw, serviceMetricSuccess(true), "service metrics success", c.logger) +} + +// writeResponse is a helper method to write resp to rw and log if there is an error writing. +// respName is the name of this response that will be used in the error log. +func writeResponse(rw http.ResponseWriter, resp []byte, respName string, logger hclog.Logger) { + _, err := rw.Write(resp) if err != nil { - c.logger.Error(fmt.Sprintf("Error writing service metrics body: %s", err.Error())) + logger.Error(fmt.Sprintf("Error writing %s: %s", respName, err.Error())) } } @@ -339,6 +370,21 @@ func (c *Command) validateFlags() error { return nil } +// non2xxCode returns true if code is not in the range of 200-299 inclusive. +func non2xxCode(code int) bool { + return code < 200 || code >= 300 +} + +// serviceMetricSuccess returns a prometheus metric line indicating +// the success of the metrics merging. +func serviceMetricSuccess(success bool) []byte { + boolAsInt := 0 + if success { + boolAsInt = 1 + } + return []byte(fmt.Sprintf("%s %d\n", prometheusServiceMetricsSuccessKey, boolAsInt)) +} + // parseConsulFlags creates Consul client command flags // from command's HTTP flags and returns them as an array of strings. func (c *Command) parseConsulFlags() []string { diff --git a/control-plane/subcommand/consul-sidecar/command_test.go b/control-plane/subcommand/consul-sidecar/command_test.go index 6ee55f8ea0..370b4afd8f 100644 --- a/control-plane/subcommand/consul-sidecar/command_test.go +++ b/control-plane/subcommand/consul-sidecar/command_test.go @@ -214,50 +214,88 @@ func TestRunSignalHandlingAllProcessesEnabled(t *testing.T) { } } -type envoyMetrics struct { +type mockEnvoyMetricsGetter struct { + respStatusCode int } -func (em *envoyMetrics) Get(url string) (resp *http.Response, err error) { +func (em *mockEnvoyMetricsGetter) Get(_ string) (resp *http.Response, err error) { response := &http.Response{} + response.StatusCode = em.respStatusCode response.Body = ioutil.NopCloser(bytes.NewReader([]byte("envoy metrics\n"))) return response, nil } -type serviceMetrics struct { - url string +// mockServiceMetricsGetter +type mockServiceMetricsGetter struct { + // reqURL is the last URL that was passed to Get(url) + reqURL string + + // respStatusCode is the status code to use for the response. + respStatusCode int } -func (sm *serviceMetrics) Get(url string) (resp *http.Response, err error) { +func (sm *mockServiceMetricsGetter) Get(url string) (resp *http.Response, err error) { + // Record the URL that we were called with. + sm.reqURL = url + response := &http.Response{} response.Body = ioutil.NopCloser(bytes.NewReader([]byte("service metrics\n"))) - sm.url = url + response.StatusCode = sm.respStatusCode + return response, nil } func TestMergedMetricsServer(t *testing.T) { cases := []struct { - name string - runEnvoyMetricsServer bool - runServiceMetricsServer bool - expectedOutput string + name string + envoyMetricsGetter *mockEnvoyMetricsGetter + serviceMetricsGetter *mockServiceMetricsGetter + expectedStatusCode int + expectedOutput string }{ { - name: "happy path: envoy and service metrics are merged", - runEnvoyMetricsServer: true, - runServiceMetricsServer: true, - expectedOutput: "envoy metrics\nservice metrics\n", + name: "happy path: envoy and service metrics are merged", + envoyMetricsGetter: &mockEnvoyMetricsGetter{ + respStatusCode: 200, + }, + serviceMetricsGetter: &mockServiceMetricsGetter{ + respStatusCode: 200, + }, + expectedStatusCode: 200, + expectedOutput: "envoy metrics\nservice metrics\nconsul_merged_service_metrics_success 1\n", }, { - name: "no service metrics", - runEnvoyMetricsServer: true, - runServiceMetricsServer: false, - expectedOutput: "envoy metrics\n", + name: "service metrics non-200", + envoyMetricsGetter: &mockEnvoyMetricsGetter{ + respStatusCode: 200, + }, + serviceMetricsGetter: &mockServiceMetricsGetter{ + respStatusCode: 404, + }, + expectedStatusCode: 200, + expectedOutput: "envoy metrics\nconsul_merged_service_metrics_success 0\n", }, { - name: "no envoy metrics", - runEnvoyMetricsServer: false, - runServiceMetricsServer: true, - expectedOutput: "", + name: "envoy metrics non-200", + envoyMetricsGetter: &mockEnvoyMetricsGetter{ + respStatusCode: 404, + }, + serviceMetricsGetter: &mockServiceMetricsGetter{ + respStatusCode: 200, + }, + expectedStatusCode: 500, + expectedOutput: "Received non-2xx status code scraping Envoy proxy metrics: 404: envoy metrics\n\n", + }, + { + name: "envoy and service metrics non-200", + envoyMetricsGetter: &mockEnvoyMetricsGetter{ + respStatusCode: 500, + }, + serviceMetricsGetter: &mockServiceMetricsGetter{ + respStatusCode: 500, + }, + expectedStatusCode: 500, + expectedOutput: "Received non-2xx status code scraping Envoy proxy metrics: 500: envoy metrics\n\n", }, } @@ -272,21 +310,11 @@ func TestMergedMetricsServer(t *testing.T) { flagServiceMetricsPort: fmt.Sprint(randomPorts[1]), flagServiceMetricsPath: "/metrics", logger: hclog.Default(), + envoyMetricsGetter: c.envoyMetricsGetter, + serviceMetricsGetter: c.serviceMetricsGetter, } server := cmd.createMergedMetricsServer() - - // Override the cmd's envoyMetricsGetter and serviceMetricsGetter - // with stubs. - em := &envoyMetrics{} - sm := &serviceMetrics{} - if c.runEnvoyMetricsServer { - cmd.envoyMetricsGetter = em - } - if c.runServiceMetricsServer { - cmd.serviceMetricsGetter = sm - } - go func() { _ = server.ListenAndServe() }() @@ -304,8 +332,8 @@ func TestMergedMetricsServer(t *testing.T) { // Verify the correct service metrics url was used. The service // metrics endpoint is only called if the Envoy metrics endpoint // call succeeds. - if c.runServiceMetricsServer && c.runEnvoyMetricsServer { - require.Equal(r, fmt.Sprintf("http://127.0.0.1:%d%s", randomPorts[1], "/metrics"), sm.url) + if c.envoyMetricsGetter.respStatusCode == 200 { + require.Equal(r, fmt.Sprintf("http://127.0.0.1:%d%s", randomPorts[1], "/metrics"), c.serviceMetricsGetter.reqURL) } }) }) diff --git a/control-plane/subcommand/controller/command.go b/control-plane/subcommand/controller/command.go index 38bc7cc0df..389421e830 100644 --- a/control-plane/subcommand/controller/command.go +++ b/control-plane/subcommand/controller/command.go @@ -193,13 +193,13 @@ func (c *Command) Run(args []string) int { setupLog.Error(err, "unable to create controller", "controller", common.Mesh) return 1 } - if err = (&controller.PartitionExportsController{ + if err = (&controller.ExportedServicesController{ ConfigEntryController: configEntryReconciler, Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controller").WithName(common.PartitionExports), + Log: ctrl.Log.WithName("controller").WithName(common.ExportedServices), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", common.PartitionExports) + setupLog.Error(err, "unable to create controller", "controller", common.ExportedServices) return 1 } if err = (&controller.ServiceRouterController{ @@ -282,11 +282,11 @@ func (c *Command) Run(args []string) int { ConsulClient: consulClient, Logger: ctrl.Log.WithName("webhooks").WithName(common.Mesh), }}) - mgr.GetWebhookServer().Register("/mutate-v1alpha1-partitionexports", - &webhook.Admission{Handler: &v1alpha1.PartitionExportsWebhook{ + mgr.GetWebhookServer().Register("/mutate-v1alpha1-exportedservices", + &webhook.Admission{Handler: &v1alpha1.ExportedServicesWebhook{ Client: mgr.GetClient(), ConsulClient: consulClient, - Logger: ctrl.Log.WithName("webhooks").WithName(common.PartitionExports), + Logger: ctrl.Log.WithName("webhooks").WithName(common.ExportedServices), ConsulMeta: consulMeta, }}) mgr.GetWebhookServer().Register("/mutate-v1alpha1-servicerouter", diff --git a/control-plane/subcommand/partition-init/command.go b/control-plane/subcommand/partition-init/command.go index 4b75f9634f..8846d42efc 100644 --- a/control-plane/subcommand/partition-init/command.go +++ b/control-plane/subcommand/partition-init/command.go @@ -153,7 +153,7 @@ func (c *Command) Run(args []string) int { c.log.Error("Error reading Partition from Consul", "name", c.flagPartitionName, "error", err.Error()) } else if partition == nil { // Retry Admin Partition creation until it succeeds, or we reach the command timeout. - _, _, err = consulClient.Partitions().Create(c.ctx, &api.AdminPartition{ + _, _, err = consulClient.Partitions().Create(c.ctx, &api.Partition{ Name: c.flagPartitionName, Description: "Created by Helm installation", }, nil) diff --git a/control-plane/subcommand/partition-init/command_ent_test.go b/control-plane/subcommand/partition-init/command_ent_test.go index 8ed91cec14..22bb9b8651 100644 --- a/control-plane/subcommand/partition-init/command_ent_test.go +++ b/control-plane/subcommand/partition-init/command_ent_test.go @@ -94,7 +94,7 @@ func TestRun_PartitionExists(t *testing.T) { require.NoError(t, err) // Create the Admin Partition before the test runs. - _, _, err = consul.Partitions().Create(context.Background(), &api.AdminPartition{Name: partitionName, Description: "Created before test"}, nil) + _, _, err = consul.Partitions().Create(context.Background(), &api.Partition{Name: partitionName, Description: "Created before test"}, nil) require.NoError(t, err) ui := cli.NewMockUi() diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index 5c12dd1523..a5daefc8f5 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -793,6 +793,12 @@ type Config struct { // createAnonymousPolicy returns whether we should create a policy for the // anonymous ACL token, i.e. queries without ACL tokens. func (c *Command) createAnonymousPolicy(isPrimary bool) bool { + // Don't try to create the anonymous policy in non-default partitions because + // non-default partitions will use the anonymous policy from the default + // partition. + if c.flagEnablePartitions && c.flagPartitionName != "default" { + return false + } // If isPrimary is not set then we're in a secondary DC. // In this case we assume that the primary datacenter has already created // the anonymous policy and attached it to the anonymous token. diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 52fb3c2b9c..44f7aca672 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 = "0.37.0" + Version = "0.38.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