diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9096b10d..a7dd56b2da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ BUG FIXES: * Helm * Update client-daemonset to include ca-cert volumeMount only when tls is enabled. [[GH-1194](https://github.com/hashicorp/consul-k8s/pull/1194)] * Update create-federation-secret-job to look up the automatically generated gossip encryption key by the right name when global.name is unset or set to something other than consul. [[GH-1196](https://github.com/hashicorp/consul-k8s/pull/1196)] + * Add Admin Partitions support to Sync Catalog **(Consul Enterprise only)**. [[GH-1180](https://github.com/hashicorp/consul-k8s/pull/1190)] ## 0.43.0 (April 21, 2022) diff --git a/acceptance/tests/partitions/partitions_test.go b/acceptance/tests/partitions/partitions_connect_test.go similarity index 99% rename from acceptance/tests/partitions/partitions_test.go rename to acceptance/tests/partitions/partitions_connect_test.go index c46f4ec2a0..a12a72b430 100644 --- a/acceptance/tests/partitions/partitions_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -24,7 +24,7 @@ const staticServerNamespace = "ns1" const staticClientNamespace = "ns2" // Test that Connect works in a default and ACLsAndAutoEncryptEnabled installations for X-Partition and in-partition networking. -func TestPartitions(t *testing.T) { +func TestPartitions_Connect(t *testing.T) { env := suite.Environment() cfg := suite.Config() @@ -138,19 +138,19 @@ func TestPartitions(t *testing.T) { caKeySecretName := fmt.Sprintf("%s-consul-ca-key", releaseName) logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) - moveSecret(t, serverClusterContext, clientClusterContext, caCertSecretName) + copySecret(t, serverClusterContext, clientClusterContext, caCertSecretName) if !c.ACLsAndAutoEncryptEnabled { // When auto-encrypt is disabled, we need both // the CA cert and CA key to be available in the clients cluster to generate client certificates and keys. logger.Logf(t, "retrieving ca key secret %s from the server cluster and applying to the client cluster", caKeySecretName) - moveSecret(t, serverClusterContext, clientClusterContext, caKeySecretName) + copySecret(t, serverClusterContext, clientClusterContext, caKeySecretName) } partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) if c.ACLsAndAutoEncryptEnabled { logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) - moveSecret(t, serverClusterContext, clientClusterContext, partitionToken) + copySecret(t, serverClusterContext, clientClusterContext, partitionToken) } partitionServiceName := fmt.Sprintf("%s-consul-partition", releaseName) @@ -630,7 +630,7 @@ func TestPartitions(t *testing.T) { } } -func moveSecret(t *testing.T, sourceContext, destContext environment.TestContext, secretName string) { +func copySecret(t *testing.T, sourceContext, destContext environment.TestContext, secretName string) { t.Helper() secret, err := sourceContext.KubernetesClient(t).CoreV1().Secrets(sourceContext.KubectlOptions(t).Namespace).Get(context.Background(), secretName, metav1.GetOptions{}) diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go new file mode 100644 index 0000000000..944ebec805 --- /dev/null +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -0,0 +1,297 @@ +package partitions + +import ( + "context" + "fmt" + "strconv" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Test that Sync Catalog works in a default and ACLsAndAutoEncryptEnabled installations for partitions. +func TestPartitions_Sync(t *testing.T) { + env := suite.Environment() + cfg := suite.Config() + + if !cfg.EnableEnterprise { + t.Skipf("skipping this test because -enable-enterprise is not set") + } + + const defaultPartition = "default" + const secondaryPartition = "secondary" + const defaultNamespace = "default" + cases := []struct { + name string + destinationNamespace string + mirrorK8S bool + ACLsAndAutoEncryptEnabled bool + }{ + { + "default destination namespace", + defaultNamespace, + false, + false, + }, + { + "default destination namespace; ACLs and auto-encrypt enabled", + defaultNamespace, + false, + true, + }, + { + "single destination namespace", + staticServerNamespace, + false, + false, + }, + { + "single destination namespace; ACLs and auto-encrypt enabled", + staticServerNamespace, + false, + true, + }, + { + "mirror k8s namespaces", + staticServerNamespace, + true, + false, + }, + { + "mirror k8s namespaces; ACLs and auto-encrypt enabled", + staticServerNamespace, + true, + true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + primaryClusterContext := env.DefaultContext(t) + secondaryClusterContext := env.Context(t, environment.SecondaryContextName) + + ctx := context.Background() + + commonHelmValues := map[string]string{ + "global.adminPartitions.enabled": "true", + + "global.enableConsulNamespaces": "true", + + "global.tls.enabled": "true", + "global.tls.httpsOnly": strconv.FormatBool(c.ACLsAndAutoEncryptEnabled), + "global.tls.enableAutoEncrypt": strconv.FormatBool(c.ACLsAndAutoEncryptEnabled), + + "global.acls.manageSystemACLs": strconv.FormatBool(c.ACLsAndAutoEncryptEnabled), + + "syncCatalog.enabled": "true", + // When mirroringK8S is set, this setting is ignored. + "syncCatalog.consulNamespaces.consulDestinationNamespace": c.destinationNamespace, + "syncCatalog.consulNamespaces.mirroringK8S": strconv.FormatBool(c.mirrorK8S), + "syncCatalog.addK8SNamespaceSuffix": "false", + + "controller.enabled": "true", + + "dns.enabled": "true", + "dns.enableRedirection": strconv.FormatBool(cfg.EnableTransparentProxy), + } + + serverHelmValues := map[string]string{ + "server.exposeGossipAndRPCPorts": "true", + } + + // On Kind, there are no load balancers but since all clusters + // share the same node network (docker bridge), we can use + // a NodePort service so that we can access node(s) in a different Kind cluster. + if cfg.UseKind { + serverHelmValues["global.adminPartitions.service.type"] = "NodePort" + serverHelmValues["global.adminPartitions.service.nodePort.https"] = "30000" + } + + releaseName := helpers.RandomName() + + helpers.MergeMaps(serverHelmValues, commonHelmValues) + + // Install the consul cluster with servers in the default kubernetes context. + primaryConsulCluster := consul.NewHelmCluster(t, serverHelmValues, primaryClusterContext, cfg, releaseName) + primaryConsulCluster.Create(t) + + // Get the TLS CA certificate and key secret from the server cluster and apply it to the client cluster. + caCertSecretName := fmt.Sprintf("%s-consul-ca-cert", releaseName) + caKeySecretName := fmt.Sprintf("%s-consul-ca-key", releaseName) + + logger.Logf(t, "retrieving ca cert secret %s from the server cluster and applying to the client cluster", caCertSecretName) + copySecret(t, primaryClusterContext, secondaryClusterContext, caCertSecretName) + + if !c.ACLsAndAutoEncryptEnabled { + // When auto-encrypt is disabled, we need both + // the CA cert and CA key to be available in the clients cluster to generate client certificates and keys. + logger.Logf(t, "retrieving ca key secret %s from the server cluster and applying to the client cluster", caKeySecretName) + copySecret(t, primaryClusterContext, secondaryClusterContext, caKeySecretName) + } + + partitionToken := fmt.Sprintf("%s-consul-partitions-acl-token", releaseName) + if c.ACLsAndAutoEncryptEnabled { + logger.Logf(t, "retrieving partition token secret %s from the server cluster and applying to the client cluster", partitionToken) + copySecret(t, primaryClusterContext, secondaryClusterContext, partitionToken) + } + + partitionServiceName := fmt.Sprintf("%s-consul-partition", releaseName) + partitionSvcAddress := k8s.ServiceHost(t, cfg, primaryClusterContext, partitionServiceName) + + k8sAuthMethodHost := k8s.KubernetesAPIServerHost(t, cfg, secondaryClusterContext) + + // Create client cluster. + clientHelmValues := map[string]string{ + "global.enabled": "false", + + "global.adminPartitions.name": secondaryPartition, + + "global.tls.caCert.secretName": caCertSecretName, + "global.tls.caCert.secretKey": "tls.crt", + + "externalServers.enabled": "true", + "externalServers.hosts[0]": partitionSvcAddress, + "externalServers.tlsServerName": "server.dc1.consul", + + "client.enabled": "true", + "client.exposeGossipPorts": "true", + "client.join[0]": partitionSvcAddress, + } + + if c.ACLsAndAutoEncryptEnabled { + // Setup partition token and auth method host if ACLs enabled. + clientHelmValues["global.acls.bootstrapToken.secretName"] = partitionToken + clientHelmValues["global.acls.bootstrapToken.secretKey"] = "token" + clientHelmValues["externalServers.k8sAuthMethodHost"] = k8sAuthMethodHost + } else { + // Provide CA key when auto-encrypt is disabled. + clientHelmValues["global.tls.caKey.secretName"] = caKeySecretName + clientHelmValues["global.tls.caKey.secretKey"] = "tls.key" + } + + if cfg.UseKind { + clientHelmValues["externalServers.httpsPort"] = "30000" + } + + helpers.MergeMaps(clientHelmValues, commonHelmValues) + + // Install the consul cluster without servers in the client cluster kubernetes context. + secondaryConsulCluster := consul.NewHelmCluster(t, clientHelmValues, secondaryClusterContext, cfg, releaseName) + secondaryConsulCluster.Create(t) + + // Ensure consul clients are created. + agentPodList, err := secondaryClusterContext.KubernetesClient(t).CoreV1().Pods(secondaryClusterContext.KubectlOptions(t).Namespace).List(ctx, metav1.ListOptions{LabelSelector: "app=consul,component=client"}) + require.NoError(t, err) + require.NotEmpty(t, agentPodList.Items) + + output, err := k8s.RunKubectlAndGetOutputE(t, secondaryClusterContext.KubectlOptions(t), "logs", agentPodList.Items[0].Name, "-n", secondaryClusterContext.KubectlOptions(t).Namespace) + require.NoError(t, err) + require.Contains(t, output, "Partition: 'secondary'") + + primaryStaticServerOpts := &terratestk8s.KubectlOptions{ + ContextName: primaryClusterContext.KubectlOptions(t).ContextName, + ConfigPath: primaryClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + secondaryStaticServerOpts := &terratestk8s.KubectlOptions{ + ContextName: secondaryClusterContext.KubectlOptions(t).ContextName, + ConfigPath: secondaryClusterContext.KubectlOptions(t).ConfigPath, + Namespace: staticServerNamespace, + } + + logger.Logf(t, "creating namespaces %s in servers cluster", staticServerNamespace) + k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, primaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) + }) + + logger.Logf(t, "creating namespaces %s in clients cluster", staticServerNamespace) + k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "create", "ns", staticServerNamespace) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, secondaryClusterContext.KubectlOptions(t), "delete", "ns", staticServerNamespace) + }) + + consulClient, _ := primaryConsulCluster.SetupConsulClient(t, c.ACLsAndAutoEncryptEnabled) + + defaultPartitionQueryOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: defaultPartition} + secondaryPartitionQueryOpts := &api.QueryOptions{Namespace: staticServerNamespace, Partition: secondaryPartition} + + if !c.mirrorK8S { + defaultPartitionQueryOpts = &api.QueryOptions{Namespace: c.destinationNamespace, Partition: defaultPartition} + secondaryPartitionQueryOpts = &api.QueryOptions{Namespace: c.destinationNamespace, Partition: secondaryPartition} + } + + // Check that the ACL token is deleted. + if c.ACLsAndAutoEncryptEnabled { + // We need to register the cleanup function before we create the deployments + // because golang will execute them in reverse order i.e. the last registered + // cleanup function will be executed first. + t.Cleanup(func() { + if c.ACLsAndAutoEncryptEnabled { + retry.Run(t, func(r *retry.R) { + tokens, _, err := consulClient.ACL().TokenList(defaultPartitionQueryOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, staticServerName) + } + + tokens, _, err = consulClient.ACL().TokenList(secondaryPartitionQueryOpts) + require.NoError(r, err) + for _, token := range tokens { + require.NotContains(r, token.Description, staticServerName) + } + }) + } + }) + } + + logger.Log(t, "creating a static-server with a service") + // create service in default partition. + k8s.DeployKustomize(t, primaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + // create service in secondary partition. + k8s.DeployKustomize(t, secondaryStaticServerOpts, cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + + logger.Log(t, "checking that the service has been synced to Consul") + var services map[string][]string + counter := &retry.Counter{Count: 20, Wait: 30 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var err error + // list services in default partition catalog. + services, _, err = consulClient.Catalog().Services(defaultPartitionQueryOpts) + require.NoError(r, err) + require.Contains(r, services, staticServerName) + if _, ok := services[staticServerName]; !ok { + r.Errorf("service '%s' is not in Consul's list of services %s in the default partition", staticServerName, services) + } + // list services in secondary partition catalog. + services, _, err = consulClient.Catalog().Services(secondaryPartitionQueryOpts) + require.NoError(r, err) + require.Contains(r, services, staticServerName) + if _, ok := services[staticServerName]; !ok { + r.Errorf("service '%s' is not in Consul's list of services %s in the secondary partition", staticServerName, services) + } + }) + // validate service in the default partition. + service, _, err := consulClient.Catalog().Service(staticServerName, "", defaultPartitionQueryOpts) + require.NoError(t, err) + require.Equal(t, 1, len(service)) + require.Equal(t, []string{"k8s"}, service[0].ServiceTags) + // validate service in the secondary partition. + service, _, err = consulClient.Catalog().Service(staticServerName, "", secondaryPartitionQueryOpts) + require.NoError(t, err) + require.Equal(t, 1, len(service)) + require.Equal(t, []string{"k8s"}, service[0].ServiceTags) + + }) + } +} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 7b4d46577b..7964ac59d7 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -190,10 +190,10 @@ spec: {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} -connect-inject=true \ + {{- end }} {{- if and .Values.externalServers.enabled .Values.externalServers.k8sAuthMethodHost }} -auth-method-host={{ .Values.externalServers.k8sAuthMethodHost }} \ {{- end }} - {{- end }} {{- if .Values.global.federation.k8sAuthMethodHost }} -auth-method-host={{ .Values.global.federation.k8sAuthMethodHost }} \ diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index ba6c347b94..a754ed73f2 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -170,6 +170,9 @@ spec: {{- if .Values.syncCatalog.consulNodeName }} -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} {{- if .Values.syncCatalog.consulPrefix}} -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ {{- end}} diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index 070835c949..726c33f80d 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -556,6 +556,24 @@ load _helpers [ "${actual}" = "true" ] } +@test "syncCatalog/Deployment: container is created when global.acls.manageSystemACLs=true and has correct command with Partitions 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.enableConsulNamespaces=true' \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.adminPartitions.name=default' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select(.name == "sync-catalog")' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r '.command | any(contains("-partition=default"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "syncCatalog/Deployment: init container is created when global.acls.manageSystemACLs=true and has correct command and environment with tls enabled and autoencrypt enabled" { cd `chart_dir` local object=$(helm template \ diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index a634e9473d..eb24b6138e 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -268,21 +268,33 @@ func (c *Command) syncRules() (string, error) { policy = "write" } {{- if .EnableNamespaces }} -operator = "write" -acl = "write" -{{- if .SyncEnableNSMirroring }} -namespace_prefix "{{ .SyncNSMirroringPrefix }}" { +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { + mesh = "write" + acl = "write" {{- else }} -namespace "{{ .SyncConsulDestNS }}" { + operator = "write" + acl = "write" {{- end }} +{{- if .SyncEnableNSMirroring }} + namespace_prefix "{{ .SyncNSMirroringPrefix }}" { +{{- else }} + namespace "{{ .SyncConsulDestNS }}" { {{- end }} - node_prefix "" { - policy = "read" - } - service_prefix "" { +{{- if .EnablePartitions }} policy = "write" - } +{{- end }} +{{- end }} + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index ba4eb88c1a..723ab55f4e 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -480,6 +480,8 @@ partition "default" { func TestSyncRules(t *testing.T) { cases := []struct { Name string + EnablePartitions bool + PartitionName string EnableNamespaces bool ConsulSyncDestinationNamespace string EnableSyncK8SNSMirroring bool @@ -489,6 +491,9 @@ func TestSyncRules(t *testing.T) { }{ { Name: "Namespaces are disabled", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: false, ConsulSyncDestinationNamespace: "sync-namespace", EnableSyncK8SNSMirroring: true, SyncK8SNSMirroringPrefix: "prefix-", @@ -496,111 +501,278 @@ func TestSyncRules(t *testing.T) { Expected: `node "k8s-sync" { policy = "write" } - node_prefix "" { - policy = "read" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + }`, + }, + { + Name: "Namespaces are disabled, non-default node name", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: false, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { + policy = "write" } - service_prefix "" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + }`, + }, + { + Name: "Namespaces are enabled, mirroring disabled", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: false, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" + } + operator = "write" + acl = "write" + namespace "sync-namespace" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } }`, }, { - Name: "Namespaces are disabled, non-default node name", + Name: "Namespaces are enabled, mirroring disabled, non-default node name", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", - EnableSyncK8SNSMirroring: true, + EnableSyncK8SNSMirroring: false, SyncK8SNSMirroringPrefix: "prefix-", SyncConsulNodeName: "new-node-name", Expected: `node "new-node-name" { policy = "write" } - node_prefix "" { - policy = "read" + operator = "write" + acl = "write" + namespace "sync-namespace" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + }`, + }, + { + Name: "Namespaces are enabled, mirroring enabled, prefix empty", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { + policy = "write" } - service_prefix "" { + operator = "write" + acl = "write" + namespace_prefix "" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + }`, + }, + { + Name: "Namespaces are enabled, mirroring enabled, prefix empty, non-default node name", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { policy = "write" + } + operator = "write" + acl = "write" + namespace_prefix "" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } }`, }, { - Name: "Namespaces are enabled, mirroring disabled", + Name: "Namespaces are enabled, mirroring enabled, prefix defined", + EnablePartitions: false, + PartitionName: "", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, SyncK8SNSMirroringPrefix: "prefix-", SyncConsulNodeName: "k8s-sync", Expected: `node "k8s-sync" { policy = "write" } -operator = "write" -acl = "write" -namespace "sync-namespace" { - node_prefix "" { - policy = "read" + operator = "write" + acl = "write" + namespace_prefix "prefix-" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + }`, + }, + { + Name: "Namespaces are enabled, mirroring enabled, prefix defined, non-default node name", + EnablePartitions: false, + PartitionName: "", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { + policy = "write" } - service_prefix "" { + operator = "write" + acl = "write" + namespace_prefix "prefix-" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + }`, + }, + { + Name: "Partitions are enabled, Namespaces are enabled, mirroring disabled", + EnablePartitions: true, + PartitionName: "foo", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: false, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" } +partition "foo" { + mesh = "write" + acl = "write" + namespace "sync-namespace" { + policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } + } }`, }, { - Name: "Namespaces are enabled, mirroring disabled, non-default node name", + Name: "Partitions are enabled, Namespaces are enabled, mirroring disabled, non-default node name", + EnablePartitions: true, + PartitionName: "foo", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: false, SyncK8SNSMirroringPrefix: "prefix-", SyncConsulNodeName: "new-node-name", Expected: `node "new-node-name" { policy = "write" } -operator = "write" -acl = "write" -namespace "sync-namespace" { - node_prefix "" { - policy = "read" - } - service_prefix "" { +partition "foo" { + mesh = "write" + acl = "write" + namespace "sync-namespace" { policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } } }`, }, { - Name: "Namespaces are enabled, mirroring enabled, prefix empty", + Name: "Partitions are enabled, Namespaces are enabled, mirroring enabled, prefix empty", + EnablePartitions: true, + PartitionName: "foo", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "", SyncConsulNodeName: "k8s-sync", Expected: `node "k8s-sync" { policy = "write" } -operator = "write" -acl = "write" -namespace_prefix "" { - node_prefix "" { - policy = "read" - } - service_prefix "" { +partition "foo" { + mesh = "write" + acl = "write" + namespace_prefix "" { policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } } }`, }, { - Name: "Namespaces are enabled, mirroring enabled, prefix empty, non-default node name", + Name: "Partitions are enabled, Namespaces are enabled, mirroring enabled, prefix empty, non-default node name", + EnablePartitions: true, + PartitionName: "foo", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "", SyncConsulNodeName: "new-node-name", Expected: `node "new-node-name" { policy = "write" } -operator = "write" -acl = "write" -namespace_prefix "" { - node_prefix "" { - policy = "read" - } - service_prefix "" { +partition "foo" { + mesh = "write" + acl = "write" + namespace_prefix "" { policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } } }`, }, { - Name: "Namespaces are enabled, mirroring enabled, prefix defined", + Name: "Partitions are enabled, Namespaces are enabled, mirroring enabled, prefix defined", + EnablePartitions: true, + PartitionName: "foo", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", EnableSyncK8SNSMirroring: true, @@ -609,19 +781,24 @@ namespace_prefix "" { Expected: `node "k8s-sync" { policy = "write" } -operator = "write" -acl = "write" -namespace_prefix "prefix-" { - node_prefix "" { - policy = "read" - } - service_prefix "" { +partition "foo" { + mesh = "write" + acl = "write" + namespace_prefix "prefix-" { policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } } }`, }, { - Name: "Namespaces are enabled, mirroring enabled, prefix defined, non-default node name", + Name: "Partitions are enabled, Namespaces are enabled, mirroring enabled, prefix defined, non-default node name", + EnablePartitions: true, + PartitionName: "foo", EnableNamespaces: true, ConsulSyncDestinationNamespace: "sync-namespace", EnableSyncK8SNSMirroring: true, @@ -630,14 +807,17 @@ namespace_prefix "prefix-" { Expected: `node "new-node-name" { policy = "write" } -operator = "write" -acl = "write" -namespace_prefix "prefix-" { - node_prefix "" { - policy = "read" - } - service_prefix "" { +partition "foo" { + mesh = "write" + acl = "write" + namespace_prefix "prefix-" { policy = "write" + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "write" + } } }`, }, @@ -646,6 +826,8 @@ namespace_prefix "prefix-" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, flagConsulSyncDestinationNamespace: tt.ConsulSyncDestinationNamespace, flagEnableSyncK8SNSMirroring: tt.EnableSyncK8SNSMirroring,