diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index b9d438c436..0b87c73c14 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -42,19 +42,26 @@ spec: path: tls.crt {{- end }} containers: - - name: post-install-job + - name: partition-create-job image: {{ .Values.global.imageK8S }} env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - {{- if .Values.global.tls.enabled }} + {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.bootstrapToken.secretName }} + key: {{ .Values.global.acls.bootstrapToken.secretKey }} + {{- end }} + {{- if .Values.global.tls.enabled }} volumeMounts: - name: consul-ca-cert mountPath: /consul/tls/ca readOnly: true - {{- end }} + {{- end }} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 752774064a..07b9ab8e6b 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -134,7 +134,10 @@ spec: -sync-consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ {{- end }} {{- end }} - + {{- if .Values.global.adminPartitions.enabled }} + -enable-partitions=true \ + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} {{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.global.enabled)) }} -allow-dns=true \ {{- end }} diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index e0560c8c60..64a9d0e4c0 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -1407,7 +1407,7 @@ rollingUpdate: #-------------------------------------------------------------------- # partitions -@test "client/DaemonSet: -partitions can be set by global.adminPartition.enabled" { +@test "client/DaemonSet: -partitions can be set by global.adminPartitions.enabled" { cd `chart_dir` local actual=$(helm template \ -s templates/client-daemonset.yaml \ @@ -1417,7 +1417,7 @@ rollingUpdate: [ "${actual}" = "true" ] } -@test "client/DaemonSet: -partitions can be overridden by global.adminPartition.name" { +@test "client/DaemonSet: -partitions can be overridden by global.adminPartitions.name" { cd `chart_dir` local actual=$(helm template \ -s templates/client-daemonset.yaml \ diff --git a/charts/consul/test/unit/partition-init-job.bats b/charts/consul/test/unit/partition-init-job.bats index ac6d5ccd27..0f0224e53d 100644 --- a/charts/consul/test/unit/partition-init-job.bats +++ b/charts/consul/test/unit/partition-init-job.bats @@ -93,4 +93,20 @@ load _helpers # check that the volume uses the provided secret key actual=$(echo $ca_cert_volume | jq -r '.secret.items[0].key' | tee /dev/stderr) [ "${actual}" = "key" ] -} \ No newline at end of file +} + +#-------------------------------------------------------------------- +# global.acls.bootstrapToken + +@test "partitionInit/Job: HTTP_TOKEN when global.acls.bootstrapToken is provided" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/partition-init-job.yaml \ + --set 'global.enabled=false' \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.acls.bootstrapToken.secretName=partition-token' \ + --set 'global.acls.bootstrapToken.secretKey=token' \ + . | tee /dev/stderr | + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 4fd6867d17..a0ae9a34c5 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -995,6 +995,45 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# admin partitions + +@test "serverACLInit/Job: admin partitions disabled by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo $object | + yq 'any(contains("enable-partitions"))' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(echo $object | + yq 'any(contains("partition"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "serverACLInit/Job: admin partitions enabled when admin partitions are enabled" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.enableConsulNamespaces=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo $object | + yq 'any(contains("enable-partitions"))' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | + yq 'any(contains("partition"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # global.acls.createReplicationToken diff --git a/control-plane/subcommand/partition-init/command.go b/control-plane/subcommand/partition-init/command.go index bdbb4df21d..4b75f9634f 100644 --- a/control-plane/subcommand/partition-init/command.go +++ b/control-plane/subcommand/partition-init/command.go @@ -23,6 +23,7 @@ type Command struct { flags *flag.FlagSet k8s *k8sflags.K8SFlags + http *flags.HTTPFlags flagPartitionName string @@ -65,7 +66,6 @@ func (c *Command) init() { "The server name to set as the SNI header when sending HTTPS requests to Consul.") c.flags.BoolVar(&c.flagUseHTTPS, "use-https", false, "Toggle for using HTTPS for all API calls to Consul.") - c.flags.DurationVar(&c.flagTimeout, "timeout", 10*time.Minute, "How long we'll try to bootstrap Partitions for before timing out, e.g. 1ms, 2s, 3m") c.flags.StringVar(&c.flagLogLevel, "log-level", "info", @@ -75,7 +75,9 @@ func (c *Command) init() { "Enable or disable JSON output format for logging.") c.k8s = &k8sflags.K8SFlags{} + c.http = &flags.HTTPFlags{} flags.Merge(c.flags, c.k8s.Flags()) + flags.Merge(c.flags, c.http.Flags()) c.help = flags.Usage(help, c.flags) // Default retry to 1s. This is exposed for setting in tests. diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index b69b66edbb..9673c977b5 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -67,6 +67,10 @@ type Command struct { flagCreateACLReplicationToken bool flagACLReplicationTokenFile string + // Flags to support partitions + flagEnablePartitions bool // true if Admin Partitions are enabled + flagPartitionName string // name of the Admin Partition + // Flags to support namespaces flagEnableNamespaces bool // Use namespacing on all components flagConsulSyncDestinationNamespace string // Consul namespace to register all catalog sync services into if not mirroring @@ -169,6 +173,11 @@ func (c *Command) init() { c.flags.BoolVar(&c.flagUseHTTPS, "use-https", false, "Toggle for using HTTPS for all API calls to Consul.") + c.flags.BoolVar(&c.flagEnablePartitions, "enable-partitions", false, + "[Enterprise Only] Enables Admin Partitions [Enterprise only feature]") + c.flags.StringVar(&c.flagPartitionName, "partition", "", + "[Enterprise Only] Name of the Admin Partition") + c.flags.BoolVar(&c.flagEnableNamespaces, "enable-namespaces", false, "[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored [Enterprise only feature]") c.flags.StringVar(&c.flagConsulSyncDestinationNamespace, "consul-sync-destination-namespace", consulDefaultNamespace, @@ -349,7 +358,7 @@ func (c *Command) Run(args []string) int { // For all of the next operations we'll need a Consul client. serverAddr := fmt.Sprintf("%s:%d", serverAddresses[0], c.flagServerPort) - consulClient, err := consul.NewClient(&api.Config{ + clientConfig := &api.Config{ Address: serverAddr, Scheme: scheme, Token: bootstrapToken, @@ -357,7 +366,12 @@ func (c *Command) Run(args []string) int { Address: c.flagConsulTLSServerName, CAFile: c.flagConsulCACert, }, - }) + } + if c.flagEnablePartitions { + clientConfig.Partition = c.flagPartitionName + } + + consulClient, err := consul.NewClient(clientConfig) if err != nil { c.log.Error(fmt.Sprintf("Error creating Consul client for addr %q: %s", serverAddr, err)) return 1 @@ -382,6 +396,15 @@ func (c *Command) Run(args []string) int { } } + if c.flagEnablePartitions && c.flagPartitionName == "default" && isPrimary { + // Partition token must be local because only the Primary datacenter can have Admin Partitions. + err := c.createLocalACL("partitions", partitionRules, consulDC, isPrimary, consulClient) + if err != nil { + c.log.Error(err.Error()) + return 1 + } + } + // If namespaces are enabled, to allow cross-Consul-namespace permissions // for services from k8s, the Consul `default` namespace needs a policy // allowing service discovery in all namespaces. Each namespace that is @@ -389,12 +412,17 @@ func (c *Command) Run(args []string) int { // connect inject) needs to reference this policy on namespace creation // to finish the cross namespace permission setup. if c.flagEnableNamespaces { + crossNamespaceRule, err := c.crossNamespaceRule() + if err != nil { + c.log.Error("Error templating cross namespace rules", "err", err) + return 1 + } policyTmpl := api.ACLPolicy{ Name: "cross-namespace-policy", Description: "Policy to allow permissions to cross Consul namespaces for k8s services", - Rules: crossNamespaceRules, + Rules: crossNamespaceRule, } - err := c.untilSucceeds(fmt.Sprintf("creating %s policy", policyTmpl.Name), + err = c.untilSucceeds(fmt.Sprintf("creating %s policy", policyTmpl.Name), func() error { return c.createOrUpdateACLPolicy(policyTmpl, consulClient) }) @@ -497,7 +525,12 @@ func (c *Command) Run(args []string) int { } if c.flagCreateEntLicenseToken { - err := c.createLocalACL("enterprise-license", entLicenseRules, consulDC, isPrimary, consulClient) + var err error + if c.flagEnablePartitions { + err = c.createLocalACL("enterprise-license", entPartitionLicenseRules, consulDC, isPrimary, consulClient) + } else { + err = c.createLocalACL("enterprise-license", entLicenseRules, consulDC, isPrimary, consulClient) + } if err != nil { c.log.Error(err.Error()) return 1 @@ -827,6 +860,12 @@ func (c *Command) validateFlags() error { ) } + if c.flagEnablePartitions && c.flagPartitionName == "" { + return errors.New("-partition must be set if -enable-partitions is true") + } + if !c.flagEnablePartitions && c.flagPartitionName != "" { + return fmt.Errorf("-enable-partitions must be 'true' if setting -partition to %s", c.flagPartitionName) + } return nil } diff --git a/control-plane/subcommand/server-acl-init/command_ent_test.go b/control-plane/subcommand/server-acl-init/command_ent_test.go index 57270644b6..96659caab8 100644 --- a/control-plane/subcommand/server-acl-init/command_ent_test.go +++ b/control-plane/subcommand/server-acl-init/command_ent_test.go @@ -19,7 +19,6 @@ import ( // and there's a single consul destination namespace. func TestRun_ConnectInject_SingleDestinationNamespace(t *testing.T) { t.Parallel() - consulDestNamespaces := []string{"default", "destination"} for _, consulDestNamespace := range consulDestNamespaces { t.Run(consulDestNamespace, func(tt *testing.T) { @@ -40,6 +39,8 @@ func TestRun_ConnectInject_SingleDestinationNamespace(t *testing.T) { "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, "-create-inject-token", + "-enable-partitions", + "-partition=default", "-enable-namespaces", "-consul-inject-destination-namespace", consulDestNamespace, "-acl-binding-rule-selector=serviceaccount.name!=default", @@ -160,6 +161,8 @@ func TestRun_ConnectInject_NamespaceMirroring(t *testing.T) { "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, "-create-inject-token", + "-enable-partitions", + "-partition=default", "-enable-namespaces", "-enable-inject-k8s-namespace-mirroring", "-inject-k8s-namespace-mirroring-prefix", c.MirroringPrefix, @@ -203,7 +206,7 @@ func TestRun_ConnectInject_NamespaceMirroring(t *testing.T) { } } -// Test that ACL policies get updated if namespaces config changes. +// Test that ACL policies get updated if namespaces/partition config changes. func TestRun_ACLPolicyUpdates(t *testing.T) { t.Parallel() @@ -234,9 +237,11 @@ func TestRun_ACLPolicyUpdates(t *testing.T) { "-terminating-gateway-name=anothergw", "-create-controller-token", } - // Our second run, we're going to update from namespaces disabled to - // namespaces enabled with a single destination ns. + // Our second run, we're going to update from partitions and namespaces disabled to + // namespaces enabled with a single destination ns and partitions enabled. secondRunArgs := append(firstRunArgs, + "-enable-partitions", + "-partition=default", "-enable-namespaces", "-consul-sync-destination-namespace=sync", "-consul-inject-destination-namespace=dest") @@ -322,6 +327,7 @@ func TestRun_ACLPolicyUpdates(t *testing.T) { "gw-terminating-gateway-token", "anothergw-terminating-gateway-token", "controller-token", + "partitions-token", } policies, _, err = consul.ACL().PolicyList(nil) require.NoError(err) @@ -348,10 +354,12 @@ func TestRun_ACLPolicyUpdates(t *testing.T) { case "connect-inject-token": // The connect inject token doesn't have namespace config, // but does change to operator:write from an empty string. - require.Contains(actRules, "operator = \"write\"") + require.Contains(actRules, "acl = \"write\"") case "client-snapshot-agent-token", "enterprise-license-token": // The snapshot agent and enterprise license tokens shouldn't change. require.NotContains(actRules, "namespace") + case "partitions-token": + require.Contains(actRules, "acl = \"write\"\noperator = \"write\"") default: // Assert that the policies have the word namespace in them. This // tests that they were updated. The actual contents are tested @@ -528,6 +536,8 @@ func TestRun_ConnectInject_Updates(t *testing.T) { "-server-port=" + strings.Split(testAgent.HTTPAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, + "-enable-partitions", + "-partition=default", "-create-inject-token", } @@ -693,6 +703,13 @@ func TestRun_TokensWithNamespacesEnabled(t *testing.T) { SecretNames: []string{resourcePrefix + "-controller-acl-token"}, LocalToken: false, }, + "partitions token": { + TokenFlags: []string{"-enable-partitions", "-partition=default"}, + PolicyNames: []string{"partitions-token"}, + PolicyDCs: []string{"dc1"}, + SecretNames: []string{resourcePrefix + "-partitions-acl-token"}, + LocalToken: true, + }, } for testName, c := range cases { t.Run(testName, func(t *testing.T) { @@ -713,6 +730,8 @@ func TestRun_TokensWithNamespacesEnabled(t *testing.T) { "-server-port", strings.Split(testSvr.HTTPAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, + "-enable-partitions", + "-partition=default", "-enable-namespaces", }, c.TokenFlags...) @@ -779,37 +798,43 @@ func TestRun_GatewayNamespaceParsing(t *testing.T) { "gateway-ingress-gateway-token", "another-gateway-ingress-gateway-token"}, ExpectedPolicies: []string{` -namespace "default" { - service "ingress" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "ingress" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, ` -namespace "default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, ` -namespace "default" { - service "another-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "another-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`}, }, @@ -822,37 +847,43 @@ namespace "default" { "gateway-ingress-gateway-token", "another-gateway-ingress-gateway-token"}, ExpectedPolicies: []string{` -namespace "default" { - service "ingress" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "ingress" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, ` -namespace "namespace1" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "namespace1" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, ` -namespace "namespace2" { - service "another-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" +partition "default" { + namespace "namespace2" { + service "another-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`}, }, @@ -865,28 +896,34 @@ namespace "namespace2" { "gateway-terminating-gateway-token", "another-gateway-terminating-gateway-token"}, ExpectedPolicies: []string{` -namespace "default" { - service "terminating" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "terminating" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, ` -namespace "default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, ` -namespace "default" { - service "another-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "another-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`}, }, @@ -899,28 +936,34 @@ namespace "default" { "gateway-terminating-gateway-token", "another-gateway-terminating-gateway-token"}, ExpectedPolicies: []string{` -namespace "default" { - service "terminating" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "default" { + service "terminating" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, ` -namespace "namespace1" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "namespace1" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, ` -namespace "namespace2" { - service "another-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" +partition "default" { + namespace "namespace2" { + service "another-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`}, }, @@ -944,6 +987,8 @@ namespace "namespace2" { "-server-port", strings.Split(testSvr.HTTPAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-enable-namespaces=true", + "-enable-partitions", + "-partition=default", }, c.TokenFlags...) responseCode := cmd.Run(cmdArgs) diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index 1b8a64b704..5620e6dcff 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -7,6 +7,8 @@ import ( ) type rulesData struct { + EnablePartitions bool + PartitionName string EnableNamespaces bool SyncConsulDestNS string SyncEnableNSMirroring bool @@ -35,28 +37,55 @@ service "consul-snapshot" { }` const entLicenseRules = `operator = "write"` +const entPartitionLicenseRules = `acl = "write"` -const crossNamespaceRules = `namespace_prefix "" { - service_prefix "" { - policy = "read" - } - node_prefix "" { - policy = "read" +const partitionRules = `acl = "write" +operator = "write" +agent_prefix "" { + policy = "read" +} +partition_prefix "" { + acl = "write" + mesh = "write" +}` + +func (c *Command) crossNamespaceRule() (string, error) { + crossNamespaceRulesTpl := `{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { +{{- end }} + namespace_prefix "" { + service_prefix "" { + policy = "read" + } + node_prefix "" { + policy = "read" + } } -} ` +{{- if .EnablePartitions }} +} +{{- end }}` + + return c.renderRules(crossNamespaceRulesTpl) +} func (c *Command) agentRules() (string, error) { agentRulesTpl := ` +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { +{{- end }} node_prefix "" { policy = "write" } {{- if .EnableNamespaces }} -namespace_prefix "" { + namespace_prefix "" { {{- end }} - service_prefix "" { - policy = "read" - } + service_prefix "" { + policy = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -80,16 +109,22 @@ func (c *Command) anonymousTokenRules() (string, error) { // ACL token. Thus the anonymous policy must // allow reading all services. anonTokenRulesTpl := ` +{{- if .EnablePartitions }} +partition_prefix "" { +{{- end }} {{- if .EnableNamespaces }} -namespace_prefix "" { + namespace_prefix "" { {{- end }} - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" - } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -133,19 +168,25 @@ namespace_prefix "" { func (c *Command) ingressGatewayRules(name, namespace string) (string, error) { ingressGatewayRulesTpl := ` +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { +{{- end }} {{- if .EnableNamespaces }} -namespace "{{ .GatewayNamespace }}" { + namespace "{{ .GatewayNamespace }}" { {{- end }} - service "{{ .GatewayName }}" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" - } + service "{{ .GatewayName }}" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -159,16 +200,22 @@ namespace "{{ .GatewayNamespace }}" { // of the initial implementation func (c *Command) terminatingGatewayRules(name, namespace string) (string, error) { terminatingGatewayRulesTpl := ` +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { +{{- end }} {{- if .EnableNamespaces }} -namespace "{{ .GatewayNamespace }}" { + namespace "{{ .GatewayNamespace }}" { {{- end }} - service "{{ .GatewayName }}" { - policy = "write" - } - node_prefix "" { - policy = "read" - } + service "{{ .GatewayName }}" { + policy = "write" + } + node_prefix "" { + policy = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -207,22 +254,34 @@ func (c *Command) injectRules() (string, error) { // The Connect injector needs permissions to create namespaces when namespaces are enabled. // It must also create/update service health checks via the endpoints controller. // When ACLs are enabled, the endpoints controller needs "acl:write" permissions - // to delete ACL tokens created via "consul login". + // to delete ACL tokens created via "consul login". policy = "write" is required when + // creating namespaces within a partition. injectRulesTpl := ` +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { + acl = "write" +{{- else }} {{- if .EnableNamespaces }} -operator = "write" + operator = "write" {{- end }} -node_prefix "" { - policy = "write" -} -{{- if .EnableNamespaces }} -namespace_prefix "" { {{- end }} - acl = "write" - service_prefix "" { + node_prefix "" { policy = "write" } {{- if .EnableNamespaces }} + namespace_prefix "" { +{{- end }} +{{- if .EnablePartitions }} + policy = "write" +{{- end }} + acl = "write" + service_prefix "" { + policy = "write" + } +{{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }}` return c.renderRules(injectRulesTpl) @@ -237,43 +296,62 @@ func (c *Command) aclReplicationRules() (string, error) { // datacenters during federation since in order to start ACL replication, // we need a token with both replication and agent permissions. aclReplicationRulesTpl := ` -operator = "write" -agent_prefix "" { - policy = "read" -} -node_prefix "" { - policy = "write" -} -{{- if .EnableNamespaces }} -namespace_prefix "" { +{{- if .EnablePartitions }} +partition "default" { {{- end }} - acl = "write" - service_prefix "" { + operator = "write" + agent_prefix "" { policy = "read" - intentions = "read" } + node_prefix "" { + policy = "write" + } +{{- if .EnableNamespaces }} + namespace_prefix "" { +{{- end }} + acl = "write" + service_prefix "" { + policy = "read" + intentions = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` return c.renderRules(aclReplicationRulesTpl) } +// policy = "write" is required when creating namespaces within a partition. func (c *Command) controllerRules() (string, error) { controllerRules := ` -operator = "write" +{{- if .EnablePartitions }} +partition "{{ .PartitionName }}" { + mesh = "write" + acl = "write" +{{- else }} + operator = "write" +{{- end }} {{- if .EnableNamespaces }} {{- if .InjectEnableNSMirroring }} -namespace_prefix "{{ .InjectNSMirroringPrefix }}" { + namespace_prefix "{{ .InjectNSMirroringPrefix }}" { {{- else }} -namespace "{{ .InjectConsulDestNS }}" { + namespace "{{ .InjectConsulDestNS }}" { {{- end }} {{- end }} - service_prefix "" { +{{- if .EnablePartitions }} policy = "write" - intentions = "write" - } +{{- end }} + service_prefix "" { + policy = "write" + intentions = "write" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -282,6 +360,8 @@ namespace "{{ .InjectConsulDestNS }}" { func (c *Command) rulesData() rulesData { return rulesData{ + EnablePartitions: c.flagEnablePartitions, + PartitionName: c.flagPartitionName, EnableNamespaces: c.flagEnableNamespaces, SyncConsulDestNS: c.flagConsulSyncDestinationNamespace, SyncEnableNSMirroring: c.flagEnableSyncK8SNSMirroring, diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 1160236eef..320ec6bda0 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -10,28 +10,48 @@ import ( func TestAgentRules(t *testing.T) { cases := []struct { Name string + EnablePartitions bool + PartitionName string EnableNamespaces bool Expected string }{ { - "Namespaces are disabled", - false, - `node_prefix "" { + Name: "Namespaces and Partitions are disabled", + Expected: ` + node_prefix "" { policy = "write" } - service_prefix "" { - policy = "read" + service_prefix "" { + policy = "read" + }`, + }, + { + Name: "Namespaces are enabled, Partitions are disabled", + EnableNamespaces: true, + Expected: ` + node_prefix "" { + policy = "write" + } + namespace_prefix "" { + service_prefix "" { + policy = "read" + } }`, }, { - "Namespaces are enabled", - true, - `node_prefix "" { + Name: "Namespaces and Partitions are enabled", + EnablePartitions: true, + PartitionName: "part-1", + EnableNamespaces: true, + Expected: ` +partition "part-1" { + node_prefix "" { policy = "write" } -namespace_prefix "" { - service_prefix "" { - policy = "read" + namespace_prefix "" { + service_prefix "" { + policy = "read" + } } }`, }, @@ -39,16 +59,16 @@ namespace_prefix "" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } agentRules, err := cmd.agentRules() - require.NoError(err) - require.Equal(tt.Expected, agentRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, agentRules) }) } } @@ -56,30 +76,48 @@ namespace_prefix "" { func TestAnonymousTokenRules(t *testing.T) { cases := []struct { Name string + EnablePartitions bool + PartitionName string EnableNamespaces bool Expected string }{ { - "Namespaces are disabled", - false, - ` - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are disabled", + Expected: ` + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + }`, + }, + { + Name: "Namespaces are enabled, Partitions are disabled", + EnableNamespaces: true, + Expected: ` + namespace_prefix "" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } }`, }, { - "Namespaces are enabled", - true, - ` -namespace_prefix "" { - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are enabled", + EnablePartitions: true, + PartitionName: "part-2", + EnableNamespaces: true, + Expected: ` +partition_prefix "" { + namespace_prefix "" { + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, }, @@ -87,16 +125,16 @@ namespace_prefix "" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } rules, err := cmd.anonymousTokenRules() - require.NoError(err) - require.Equal(tt.Expected, rules) + require.NoError(t, err) + require.Equal(t, tt.Expected, rules) }) } } @@ -108,9 +146,8 @@ func TestMeshGatewayRules(t *testing.T) { Expected string }{ { - "Namespaces are disabled", - false, - `agent_prefix "" { + Name: "Namespaces are disabled", + Expected: `agent_prefix "" { policy = "read" } service "mesh-gateway" { @@ -124,9 +161,9 @@ func TestMeshGatewayRules(t *testing.T) { }`, }, { - "Namespaces are enabled", - true, - `agent_prefix "" { + Name: "Namespaces are enabled", + EnableNamespaces: true, + Expected: `agent_prefix "" { policy = "read" } namespace "default" { @@ -147,16 +184,14 @@ namespace_prefix "" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, } meshGatewayRules, err := cmd.meshGatewayRules() - require.NoError(err) - require.Equal(tt.Expected, meshGatewayRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, meshGatewayRules) }) } } @@ -166,58 +201,102 @@ func TestIngressGatewayRules(t *testing.T) { Name string GatewayName string GatewayNamespace string + EnablePartitions bool + PartitionName string EnableNamespaces bool Expected string }{ { - "Namespaces are disabled", - "ingress-gateway", - "", - false, - ` - service "ingress-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are disabled", + GatewayName: "ingress-gateway", + Expected: ` + service "ingress-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + }`, + }, + { + Name: "Namespaces are enabled, Partitions are disabled", + GatewayName: "gateway", + GatewayNamespace: "default", + EnableNamespaces: true, + Expected: ` + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } }`, }, { - "Namespaces are enabled", - "gateway", - "default", - true, - ` -namespace "default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" + Name: "Namespaces are enabled, non-default namespace, Partitions are disabled", + GatewayName: "gateway", + GatewayNamespace: "non-default", + EnableNamespaces: true, + Expected: ` + namespace "non-default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } + }`, + }, + { + Name: "Namespaces and Partitions are enabled", + GatewayName: "gateway", + GatewayNamespace: "default", + EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "part-1", + Expected: ` +partition "part-1" { + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, }, { - "Namespaces are enabled, non-default namespace", - "gateway", - "non-default", - true, - ` -namespace "non-default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" - } - service_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are enabled, non-default namespace", + GatewayName: "gateway", + GatewayNamespace: "non-default", + EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "default", + Expected: ` +partition "default" { + namespace "non-default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + service_prefix "" { + policy = "read" + } } }`, }, @@ -225,16 +304,16 @@ namespace "non-default" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } ingressGatewayRules, err := cmd.ingressGatewayRules(tt.GatewayName, tt.GatewayNamespace) - require.NoError(err) - require.Equal(tt.Expected, ingressGatewayRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, ingressGatewayRules) }) } } @@ -245,48 +324,86 @@ func TestTerminatingGatewayRules(t *testing.T) { GatewayName string GatewayNamespace string EnableNamespaces bool + EnablePartitions bool + PartitionName string Expected string }{ { - "Namespaces are disabled", - "terminating-gateway", - "", - false, - ` - service "terminating-gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are disabled", + GatewayName: "terminating-gateway", + Expected: ` + service "terminating-gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + }`, + }, + { + Name: "Namespaces are enabled, Partitions are disabled", + GatewayName: "gateway", + GatewayNamespace: "default", + EnableNamespaces: true, + Expected: ` + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } }`, }, { - "Namespaces are enabled", - "gateway", - "default", - true, - ` -namespace "default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" + Name: "Namespaces are enabled, non-default namespace, Partitions are disabled", + GatewayName: "gateway", + GatewayNamespace: "non-default", + EnableNamespaces: true, + Expected: ` + namespace "non-default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } + }`, + }, + { + Name: "Namespaces and Partitions are enabled", + GatewayName: "gateway", + GatewayNamespace: "default", + EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "part-1", + Expected: ` +partition "part-1" { + namespace "default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, }, { - "Namespaces are enabled, non-default namespace", - "gateway", - "non-default", - true, - ` -namespace "non-default" { - service "gateway" { - policy = "write" - } - node_prefix "" { - policy = "read" + Name: "Namespaces and Partitions are enabled, non-default namespace", + GatewayName: "gateway", + GatewayNamespace: "non-default", + EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "default", + Expected: ` +partition "default" { + namespace "non-default" { + service "gateway" { + policy = "write" + } + node_prefix "" { + policy = "read" + } } }`, }, @@ -294,16 +411,16 @@ namespace "non-default" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } terminatingGatewayRules, err := cmd.terminatingGatewayRules(tt.GatewayName, tt.GatewayNamespace) - require.NoError(err) - require.Equal(tt.Expected, terminatingGatewayRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, terminatingGatewayRules) }) } } @@ -319,13 +436,12 @@ func TestSyncRules(t *testing.T) { Expected string }{ { - "Namespaces are disabled", - false, - "sync-namespace", - true, - "prefix-", - "k8s-sync", - `node "k8s-sync" { + Name: "Namespaces are disabled", + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" } node_prefix "" { @@ -336,13 +452,12 @@ func TestSyncRules(t *testing.T) { }`, }, { - "Namespaces are disabled, non-default node name", - false, - "sync-namespace", - true, - "prefix-", - "new-node-name", - `node "new-node-name" { + Name: "Namespaces are disabled, non-default node name", + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { policy = "write" } node_prefix "" { @@ -353,13 +468,12 @@ func TestSyncRules(t *testing.T) { }`, }, { - "Namespaces are enabled, mirroring disabled", - true, - "sync-namespace", - false, - "prefix-", - "k8s-sync", - `node "k8s-sync" { + Name: "Namespaces are enabled, mirroring disabled", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" } operator = "write" @@ -373,13 +487,12 @@ namespace "sync-namespace" { }`, }, { - "Namespaces are enabled, mirroring disabled, non-default node name", - true, - "sync-namespace", - false, - "prefix-", - "new-node-name", - `node "new-node-name" { + Name: "Namespaces are enabled, mirroring disabled, non-default node name", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { policy = "write" } operator = "write" @@ -393,13 +506,12 @@ namespace "sync-namespace" { }`, }, { - "Namespaces are enabled, mirroring enabled, prefix empty", - true, - "sync-namespace", - true, - "", - "k8s-sync", - `node "k8s-sync" { + Name: "Namespaces are enabled, mirroring enabled, prefix empty", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" } operator = "write" @@ -413,13 +525,12 @@ namespace_prefix "" { }`, }, { - "Namespaces are enabled, mirroring enabled, prefix empty, non-default node name", - true, - "sync-namespace", - true, - "", - "new-node-name", - `node "new-node-name" { + Name: "Namespaces are enabled, mirroring enabled, prefix empty, non-default node name", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { policy = "write" } operator = "write" @@ -433,13 +544,13 @@ namespace_prefix "" { }`, }, { - "Namespaces are enabled, mirroring enabled, prefix defined", - true, - "sync-namespace", - true, - "prefix-", - "k8s-sync", - `node "k8s-sync" { + Name: "Namespaces are enabled, mirroring enabled, prefix defined", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "k8s-sync", + Expected: `node "k8s-sync" { policy = "write" } operator = "write" @@ -453,13 +564,13 @@ namespace_prefix "prefix-" { }`, }, { - "Namespaces are enabled, mirroring enabled, prefix defined, non-default node name", - true, - "sync-namespace", - true, - "prefix-", - "new-node-name", - `node "new-node-name" { + Name: "Namespaces are enabled, mirroring enabled, prefix defined, non-default node name", + EnableNamespaces: true, + ConsulSyncDestinationNamespace: "sync-namespace", + EnableSyncK8SNSMirroring: true, + SyncK8SNSMirroringPrefix: "prefix-", + SyncConsulNodeName: "new-node-name", + Expected: `node "new-node-name" { policy = "write" } operator = "write" @@ -476,8 +587,6 @@ namespace_prefix "prefix-" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, flagConsulSyncDestinationNamespace: tt.ConsulSyncDestinationNamespace, @@ -488,8 +597,8 @@ namespace_prefix "prefix-" { syncRules, err := cmd.syncRules() - require.NoError(err) - require.Equal(tt.Expected, syncRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, syncRules) }) } } @@ -498,48 +607,72 @@ namespace_prefix "prefix-" { func TestInjectRules(t *testing.T) { cases := []struct { EnableNamespaces bool + EnablePartitions bool + PartitionName string Expected string }{ { EnableNamespaces: false, + EnablePartitions: false, Expected: ` -node_prefix "" { - policy = "write" -} - acl = "write" - service_prefix "" { + node_prefix "" { + policy = "write" + } + acl = "write" + service_prefix "" { + policy = "write" + }`, + }, + { + EnableNamespaces: true, + EnablePartitions: false, + Expected: ` + operator = "write" + node_prefix "" { policy = "write" + } + namespace_prefix "" { + acl = "write" + service_prefix "" { + policy = "write" + } }`, }, { EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "part-1", Expected: ` -operator = "write" -node_prefix "" { - policy = "write" -} -namespace_prefix "" { +partition "part-1" { acl = "write" - service_prefix "" { + node_prefix "" { + policy = "write" + } + namespace_prefix "" { policy = "write" + acl = "write" + service_prefix "" { + policy = "write" + } } }`, }, } for _, tt := range cases { - caseName := fmt.Sprintf("ns=%t", tt.EnableNamespaces) + caseName := fmt.Sprintf("ns=%t, partition=%t", tt.EnableNamespaces, tt.EnablePartitions) t.Run(caseName, func(t *testing.T) { - require := require.New(t) cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } injectorRules, err := cmd.injectRules() - require.NoError(err) - require.Equal(tt.Expected, injectorRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, injectorRules) }) } } @@ -548,39 +681,65 @@ func TestReplicationTokenRules(t *testing.T) { cases := []struct { Name string EnableNamespaces bool + EnablePartitions bool + PartitionName string Expected string }{ { - "Namespaces are disabled", - false, - `operator = "write" -agent_prefix "" { - policy = "read" -} -node_prefix "" { - policy = "write" -} - acl = "write" - service_prefix "" { + Name: "Namespaces and Partitions are disabled", + Expected: ` + operator = "write" + agent_prefix "" { policy = "read" - intentions = "read" + } + node_prefix "" { + policy = "write" + } + acl = "write" + service_prefix "" { + policy = "read" + intentions = "read" + }`, + }, + { + Name: "Namespaces are enabled, Partitions are disabled", + EnableNamespaces: true, + Expected: ` + operator = "write" + agent_prefix "" { + policy = "read" + } + node_prefix "" { + policy = "write" + } + namespace_prefix "" { + acl = "write" + service_prefix "" { + policy = "read" + intentions = "read" + } }`, }, { - "Namespaces are enabled", - true, - `operator = "write" -agent_prefix "" { - policy = "read" -} -node_prefix "" { - policy = "write" -} -namespace_prefix "" { - acl = "write" - service_prefix "" { + Name: "Namespaces and Partitions are enabled, default partition", + EnableNamespaces: true, + EnablePartitions: true, + PartitionName: "default", + Expected: ` +partition "default" { + operator = "write" + agent_prefix "" { policy = "read" - intentions = "read" + } + node_prefix "" { + policy = "write" + } + namespace_prefix "" { + acl = "write" + service_prefix "" { + policy = "read" + intentions = "read" + } } }`, }, @@ -588,13 +747,14 @@ namespace_prefix "" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) cmd := Command{ + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, flagEnableNamespaces: tt.EnableNamespaces, } replicationTokenRules, err := cmd.aclReplicationRules() - require.NoError(err) - require.Equal(tt.Expected, replicationTokenRules) + require.NoError(t, err) + require.Equal(t, tt.Expected, replicationTokenRules) }) } } @@ -602,6 +762,8 @@ namespace_prefix "" { func TestControllerRules(t *testing.T) { cases := []struct { Name string + EnablePartitions bool + PartitionName string EnableNamespaces bool DestConsulNS string Mirroring bool @@ -609,48 +771,109 @@ func TestControllerRules(t *testing.T) { Expected string }{ { - Name: "namespaces=disabled", - EnableNamespaces: false, - Expected: `operator = "write" - service_prefix "" { - policy = "write" - intentions = "write" + Name: "namespaces=disabled, partitions=disabled", + Expected: ` + operator = "write" + service_prefix "" { + policy = "write" + intentions = "write" + }`, + }, + { + Name: "namespaces=enabled, consulDestNS=consul, partitions=disabled", + EnableNamespaces: true, + DestConsulNS: "consul", + Expected: ` + operator = "write" + namespace "consul" { + service_prefix "" { + policy = "write" + intentions = "write" + } + }`, + }, + { + Name: "namespaces=enabled, mirroring=true, partitions=disabled", + EnableNamespaces: true, + Mirroring: true, + Expected: ` + operator = "write" + namespace_prefix "" { + service_prefix "" { + policy = "write" + intentions = "write" + } + }`, + }, + { + Name: "namespaces=enabled, mirroring=true, mirroringPrefix=prefix-, partitions=disabled", + EnableNamespaces: true, + Mirroring: true, + MirroringPrefix: "prefix-", + Expected: ` + operator = "write" + namespace_prefix "prefix-" { + service_prefix "" { + policy = "write" + intentions = "write" + } }`, }, { - Name: "namespaces=enabled, consulDestNS=consul", + Name: "namespaces=enabled, consulDestNS=consul, partitions=enabled", + EnablePartitions: true, + PartitionName: "part-1", EnableNamespaces: true, DestConsulNS: "consul", - Expected: `operator = "write" -namespace "consul" { - service_prefix "" { + Expected: ` +partition "part-1" { + mesh = "write" + acl = "write" + namespace "consul" { policy = "write" - intentions = "write" + service_prefix "" { + policy = "write" + intentions = "write" + } } }`, }, { - Name: "namespaces=enabled, mirroring=true", + Name: "namespaces=enabled, mirroring=true, partitions=enabled", + EnablePartitions: true, + PartitionName: "part-1", EnableNamespaces: true, Mirroring: true, - Expected: `operator = "write" -namespace_prefix "" { - service_prefix "" { + Expected: ` +partition "part-1" { + mesh = "write" + acl = "write" + namespace_prefix "" { policy = "write" - intentions = "write" + service_prefix "" { + policy = "write" + intentions = "write" + } } }`, }, { - Name: "namespaces=enabled, mirroring=true, mirroringPrefix=prefix-", + Name: "namespaces=enabled, mirroring=true, mirroringPrefix=prefix-, partitions=enabled", + EnablePartitions: true, + PartitionName: "part-1", EnableNamespaces: true, Mirroring: true, MirroringPrefix: "prefix-", - Expected: `operator = "write" -namespace_prefix "prefix-" { - service_prefix "" { + Expected: ` +partition "part-1" { + mesh = "write" + acl = "write" + namespace_prefix "prefix-" { policy = "write" - intentions = "write" + service_prefix "" { + policy = "write" + intentions = "write" + } } }`, }, @@ -658,19 +881,19 @@ namespace_prefix "prefix-" { for _, tt := range cases { t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, flagConsulInjectDestinationNamespace: tt.DestConsulNS, flagEnableInjectK8SNSMirroring: tt.Mirroring, flagInjectK8SNSMirroringPrefix: tt.MirroringPrefix, + flagEnablePartitions: tt.EnablePartitions, + flagPartitionName: tt.PartitionName, } rules, err := cmd.controllerRules() - require.NoError(err) - require.Equal(tt.Expected, rules) + require.NoError(t, err) + require.Equal(t, tt.Expected, rules) }) } }