diff --git a/.changelog/4468.txt b/.changelog/4468.txt new file mode 100644 index 0000000000..2e28cf4656 --- /dev/null +++ b/.changelog/4468.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Fixed bug in TerminatingGateway controller workflow for handling AdminPartition enabled cluster ACL policies for associated TerminatingGateway services ([NET-12039](https://hashicorp.atlassian.net/browse/NET-12039)). +``` diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 25b00f275f..f58deee177 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1438,15 +1438,15 @@ server: # @type: string caCert: null - # A list of extra environment variables to set on the snapshot agent specifically - # This could be used to configure credentials that the rest of the - # stateful set would not need access to, like GOOGLE_APPLICATION_CREDENTIALS + # A list of extra environment variables to set on the snapshot agent specifically. + # Use this parameter to configure credentials that the rest of the + # stateful set would not need access to, like `GOOGLE_APPLICATION_CREDENTIALS`. # @type: map extraEnvironmentVars: { } - # A list of extra volumes to mount onto the snapshot agent. This - # is useful for bringing in extra data that only the snapshot agent needs access - # to. Like storage credentials. The value of this should be a list of objects. + # A list of extra volumes to mount onto the snapshot agent. Use this block + # to include extra data that only the snapshot agent needs access + # to, such as storage credentials. This value should be a list of objects. # # Example: # @@ -1458,10 +1458,10 @@ server: # # Each object supports the following keys: # - # - `type` - Type of the volume, must be one of "configMap" or "secret". Case sensitive. + # - `type` - Type of the volume. Must be one of `configMap` or `secret`. Case sensitive. # - # - `name` - Name of the configMap or secret to be mounted. This also controls - # the path that it is mounted to. The volume will be mounted to `/consul/userconfig/`. + # - `name` - Name of the configMap or secret to mount. This specification also controls + # the path it mounts to. The volume will be mounted to `/consul/userconfig/`. # # The snapshot agent will not attempt to load any volumes passed in this stanza # @type: array diff --git a/control-plane/controllers/configentries/configentry_controller.go b/control-plane/controllers/configentries/configentry_controller.go index dc68aea619..1456ee1921 100644 --- a/control-plane/controllers/configentries/configentry_controller.go +++ b/control-plane/controllers/configentries/configentry_controller.go @@ -90,6 +90,15 @@ type ConfigEntryController struct { // `k8s-default` namespace. NSMirroringPrefix string + // EnableConsulAdminPartitions indicates that the cluster is running an + // Enterprise binary with Admin Partitions turned on. + EnableConsulAdminPartitions bool + + // ConsulPartition is the name of the admin partition this controller writes + // to. It’s used by reconcile-time helpers to set Query/Write options and to + // stamp metadata on the config-entry. + ConsulPartition string + // CrossNSACLPolicy is the name of the ACL policy to attach to // any created Consul namespaces to allow cross namespace service discovery. // Only necessary if ACLs are enabled. @@ -147,9 +156,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont if containsString(configEntry.GetFinalizers(), FinalizerName) { logger.Info("deletion event") // Check to see if consul has config entry with the same name - entry, _, err := consulClient.ConfigEntries().Get(configEntry.ConsulKind(), configEntry.ConsulName(), &capi.QueryOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + entry, _, err := consulClient.ConfigEntries().Get(configEntry.ConsulKind(), configEntry.ConsulName(), r.queryOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) // Ignore the error where the config entry isn't found in Consul. // It is indicative of desired state. @@ -158,9 +165,11 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont } else if err == nil { // Only delete the resource from Consul if it is owned by our datacenter. if entry.GetMeta()[common.DatacenterKey] == r.DatacenterName { - _, err := consulClient.ConfigEntries().Delete(configEntry.ConsulKind(), configEntry.ConsulName(), &capi.WriteOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + //_, err := consulClient.ConfigEntries().Delete(configEntry.ConsulKind(), configEntry.ConsulName(), &capi.WriteOptions{ + // Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), + //}) + _, err = consulClient.ConfigEntries().Delete(configEntry.ConsulKind(), configEntry.ConsulName(), + r.writeOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) if err != nil { return r.syncFailed(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("deleting config entry from consul: %w", err)) @@ -183,9 +192,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont } // Check to see if consul has config entry with the same name - entryFromConsul, _, err := consulClient.ConfigEntries().Get(configEntry.ConsulKind(), configEntry.ConsulName(), &capi.QueryOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + entryFromConsul, _, err := consulClient.ConfigEntries().Get(configEntry.ConsulKind(), configEntry.ConsulName(), r.queryOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) // If a config entry with this name does not exist if isNotFoundErr(err) { logger.Info("config entry not found in consul") @@ -205,9 +212,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont } // Create the config entry - _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, &capi.WriteOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, r.writeOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) if err != nil { return r.syncFailed(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("writing config entry to consul: %w", err)) @@ -248,9 +253,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont r.nonMatchingMigrationError(configEntry, entryFromConsul)) case !matchesConsul: logger.Info("config entry does not match consul", "modify-index", entryFromConsul.GetModifyIndex()) - _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, &capi.WriteOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, r.writeOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) if err != nil { return r.syncUnknownWithError(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("updating config entry in consul: %w", err)) @@ -262,9 +265,7 @@ func (r *ConfigEntryController) ReconcileEntry(ctx context.Context, crdCtrl Cont // matches the entry in Kubernetes. We just need to update the metadata // of the entry in Consul to say that it's now managed by Kubernetes. logger.Info("migrating config entry to be managed by Kubernetes") - _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, &capi.WriteOptions{ - Namespace: r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()), - }) + _, writeMeta, err := consulClient.ConfigEntries().Set(consulEntry, r.writeOpts(r.consulNamespace(consulEntry, configEntry.ConsulMirroringNS(), configEntry.ConsulGlobalResource()))) if err != nil { return r.syncUnknownWithError(ctx, logger, crdCtrl, configEntry, ConsulAgentError, fmt.Errorf("updating config entry in consul: %w", err)) @@ -495,3 +496,27 @@ func sourceDatacenterMismatchErr(sourceDatacenter string) error { } return fmt.Errorf("config entry managed in different datacenter: %q", sourceDatacenter) } + +// queryOpts builds a *capi.QueryOptions that always includes Namespace + +// (optionally) Partition. +func (r *ConfigEntryController) queryOpts(ns string) *capi.QueryOptions { + opts := &capi.QueryOptions{ + Namespace: ns, + } + if r.EnableConsulAdminPartitions && r.ConsulPartition != "" { + // Only add ?partition=… for Enterprise clusters + opts.Partition = r.ConsulPartition + } + return opts +} + +// writeOpts mirrors queryOpts for writes. +func (r *ConfigEntryController) writeOpts(ns string) *capi.WriteOptions { + opts := &capi.WriteOptions{ + Namespace: ns, + } + if r.EnableConsulAdminPartitions && r.ConsulPartition != "" { + opts.Partition = r.ConsulPartition + } + return opts +} diff --git a/control-plane/controllers/configentries/configentry_controller_ent_test.go b/control-plane/controllers/configentries/configentry_controller_ent_test.go index 0d2c588c5c..3931b693c3 100644 --- a/control-plane/controllers/configentries/configentry_controller_ent_test.go +++ b/control-plane/controllers/configentries/configentry_controller_ent_test.go @@ -30,6 +30,12 @@ import ( "github.com/hashicorp/consul-k8s/control-plane/helper/test" ) +const ( + kubeNS = "default" + partitionName = "default" + nonDefaultPartition = "non-default" +) + // NOTE: We're not testing each controller type here because that's mostly done in // the OSS tests and it would result in too many permutations. Instead // we're only testing with the ServiceDefaults and ProxyDefaults configentries which @@ -39,7 +45,6 @@ import ( func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { t.Parallel() - kubeNS := "default" cases := []struct { kubeKind string @@ -76,6 +81,7 @@ func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -164,6 +170,7 @@ func TestConfigEntryController_createsEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -292,6 +299,7 @@ func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -384,6 +392,7 @@ func TestConfigEntryController_updatesEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -529,6 +538,7 @@ func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -612,6 +622,7 @@ func TestConfigEntryController_deletesEntConfigEntry(t *testing.T) { ConsulClientConfig: cfg, ConsulServerConnMgr: watcher, DatacenterName: datacenterName, + ConsulPartition: partitionName, }, } }, @@ -1404,3 +1415,248 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T } } } + +func TestConfigEntryController_createsConfigEntry_consulPartitions(t *testing.T) { + t.Parallel() + + svcDefaults := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{Protocol: "http"}, + } + + run := func(t *testing.T, obj common.ConfigEntryResource) { + t.Helper() + req := require.New(t) + + scheme := runtime.NewScheme() + scheme.AddKnownTypes(v1alpha1.GroupVersion, obj) + + fclient := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(obj). + WithStatusSubresource(obj). + Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + consulClient := testClient.APIClient + consulClient.Partitions().Create( + context.Background(), + &capi.Partition{Name: nonDefaultPartition}, + nil, + ) + + ceCtrl := &ConfigEntryController{ + ConsulClientConfig: testClient.Cfg, + ConsulServerConnMgr: testClient.Watcher, + DatacenterName: datacenterName, + EnableConsulAdminPartitions: true, + ConsulPartition: nonDefaultPartition, + } + + r := &ServiceDefaultsController{ + Client: fclient, + Log: logrtest.NewTestLogger(t), + Scheme: scheme, + ConfigEntryController: ceCtrl, + } + + _, err := r.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: obj.GetObjectMeta().Namespace, + Name: obj.KubernetesName(), + }, + }) + req.NoError(err) + + // Verify object landed in the correct partition. + got, _, err := consulClient.ConfigEntries().Get( + obj.ToConsul(datacenterName).GetKind(), + obj.ConsulName(), + &capi.QueryOptions{Partition: nonDefaultPartition}, + ) + req.NoError(err) + req.Equal(obj.ConsulName(), got.GetName()) + + // Status should be Synced. + err = fclient.Get(context.Background(), + types.NamespacedName{Namespace: obj.GetObjectMeta().Namespace, Name: obj.KubernetesName()}, + obj, + ) + req.NoError(err) + req.Equal(corev1.ConditionTrue, obj.SyncedConditionStatus()) + } + + t.Run("ServiceDefaults", func(t *testing.T) { run(t, svcDefaults) }) +} + +func TestConfigEntryController_updatesConfigEntry_consulPartitions(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + obj := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + Finalizers: []string{FinalizerName}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{Protocol: "http"}, + } + + scheme := runtime.NewScheme() + scheme.AddKnownTypes(v1alpha1.GroupVersion, obj) + fclient := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(obj). + WithStatusSubresource(obj).Build() + + testClient := test.TestServerWithMockConnMgrWatcher(t, nil) + consulClient := testClient.APIClient + consulClient.Partitions().Create( + context.Background(), + &capi.Partition{Name: nonDefaultPartition}, + nil, + ) + r := &ServiceDefaultsController{ + Client: fclient, + Log: logrtest.NewTestLogger(t), + Scheme: scheme, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: testClient.Cfg, + ConsulServerConnMgr: testClient.Watcher, + DatacenterName: datacenterName, + EnableConsulAdminPartitions: true, + ConsulPartition: nonDefaultPartition, + }, + } + + // First reconcile → create + _, err := r.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: kubeNS, Name: obj.KubernetesName()}, + }) + require.NoError(t, err) + + // --------------------------------------------------------------------- + // FETCH A FRESH COPY (so we have the current resourceVersion) + // --------------------------------------------------------------------- + var fresh v1alpha1.ServiceDefaults + require.NoError(t, fclient.Get(ctx, + types.NamespacedName{ + Namespace: kubeNS, + Name: obj.KubernetesName(), + }, &fresh)) + + // Mutate & update using the fresh object + fresh.Spec.Protocol = "tcp" + require.NoError(t, fclient.Update(ctx, &fresh)) + + // Second reconcile → should perform the update path + _, err = r.Reconcile(ctx, ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: kubeNS, + Name: obj.KubernetesName(), + }, + }) + require.NoError(t, err) + + // Assert the change reached Consul in the *part-1* partition + ce, _, err := consulClient.ConfigEntries().Get( + capi.ServiceDefaults, + obj.ConsulName(), + &capi.QueryOptions{Partition: nonDefaultPartition}, + ) + require.NoError(t, err) + require.Equal(t, "tcp", ce.(*capi.ServiceConfigEntry).Protocol) +} + +func TestConfigEntryController_deletesConfigEntry_consulPartitions(t *testing.T) { + t.Parallel() + + // K8s object that is already marked for deletion. + obj := &v1alpha1.ServiceDefaults{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: kubeNS, + Finalizers: []string{FinalizerName}, + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + }, + Spec: v1alpha1.ServiceDefaultsSpec{ + Protocol: "http", + }, + } + + // --------------------------------------------------------------------- + // Fake k8s client & scheme + // --------------------------------------------------------------------- + scheme := runtime.NewScheme() + scheme.AddKnownTypes(v1alpha1.GroupVersion, obj) + fclient := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(obj). + WithStatusSubresource(obj). + Build() + + // --------------------------------------------------------------------- + // Consul test server (create partition + seed entry) + // --------------------------------------------------------------------- + tc := test.TestServerWithMockConnMgrWatcher(t, nil) + + // create the partition so CRUD calls succeed + consulClient := tc.APIClient + consulClient.Partitions().Create( + context.Background(), + &capi.Partition{Name: nonDefaultPartition}, + nil, + ) + + // seed the entry inside that partition + _, _, err := consulClient.ConfigEntries().Set(&capi.ServiceConfigEntry{ + Kind: capi.ServiceDefaults, + Name: obj.KubernetesName(), + Partition: nonDefaultPartition, + Protocol: "http", + Meta: map[string]string{ + common.DatacenterKey: datacenterName, + }, + }, &capi.WriteOptions{Partition: nonDefaultPartition}) + require.NoError(t, err) + + // --------------------------------------------------------------------- + // Controller under test + // --------------------------------------------------------------------- + r := &ServiceDefaultsController{ + Client: fclient, + Log: logrtest.NewTestLogger(t), + Scheme: scheme, + ConfigEntryController: &ConfigEntryController{ + ConsulClientConfig: tc.Cfg, + ConsulServerConnMgr: tc.Watcher, + DatacenterName: datacenterName, + EnableConsulAdminPartitions: true, + ConsulPartition: nonDefaultPartition, + }, + } + + // --------------------------------------------------------------------- + // Reconcile – should delete the entry from Consul + // --------------------------------------------------------------------- + _, err = r.Reconcile(context.Background(), ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: obj.Namespace, + Name: obj.KubernetesName(), + }, + }) + require.NoError(t, err) + + // Verify the config-entry is gone from the partition. + _, _, err = consulClient.ConfigEntries().Get( + capi.ServiceDefaults, + obj.KubernetesName(), + &capi.QueryOptions{Partition: nonDefaultPartition}, + ) + require.Error(t, err) // 404 expected – entry should no longer exist +} diff --git a/control-plane/controllers/configentries/terminatinggateway_controller.go b/control-plane/controllers/configentries/terminatinggateway_controller.go index ec329bd17c..255e7e5573 100644 --- a/control-plane/controllers/configentries/terminatinggateway_controller.go +++ b/control-plane/controllers/configentries/terminatinggateway_controller.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/consul-k8s/control-plane/api/common" "strings" "text/template" @@ -36,6 +37,7 @@ type TerminatingGatewayController struct { FinalizerPatcher NamespacesEnabled bool + PartitionsEnabled bool Log logr.Logger Scheme *runtime.Scheme @@ -49,33 +51,49 @@ func init() { type templateArgs struct { Namespace string + Partition string ServiceName string EnableNamespaces bool + EnablePartitions bool } var ( servicePolicyTpl *template.Template servicePolicyRulesTpl = ` +{{- if .EnablePartitions }} +partition "{{.Partition}}" { +{{- end }} {{- if .EnableNamespaces }} -namespace "{{.Namespace}}" { + namespace "{{.Namespace}}" { {{- end }} - service "{{.ServiceName}}" { - policy = "write" - } + service "{{.ServiceName}}" { + policy = "write" + intention = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` wildcardPolicyTpl *template.Template wildcardPolicyRulesTpl = ` +{{- if .EnablePartitions }} +partition "{{.Partition}}" { +{{- end }} {{- if .EnableNamespaces }} -namespace "{{.Namespace}}" { + namespace "{{.Namespace}}" { {{- end }} - service_prefix "" { - policy = "write" - } + service_prefix "" { + policy = "write" + intention = "read" + } {{- if .EnableNamespaces }} + } +{{- end }} +{{- if .EnablePartitions }} } {{- end }} ` @@ -104,7 +122,7 @@ func (r *TerminatingGatewayController) Reconcile(ctx context.Context, req ctrl.R } if enabled { - err := r.updateACls(log, termGW) + err = r.updateACls(log, termGW) if err != nil { log.Error(err, "error updating terminating-gateway roles") r.UpdateStatusFailedToSetACLs(ctx, termGW, err) @@ -165,13 +183,20 @@ func (r *TerminatingGatewayController) aclsEnabled() (bool, error) { return state.Token != "", nil } +func (r *TerminatingGatewayController) adminPartition() string { + if r.ConfigEntryController == nil { + return common.DefaultConsulPartition + } + return defaultIfEmpty(r.ConfigEntryController.ConsulPartition) +} + func (r *TerminatingGatewayController) updateACls(log logr.Logger, termGW *consulv1alpha1.TerminatingGateway) error { - client, err := consul.NewClientFromConnMgr(r.ConfigEntryController.ConsulClientConfig, r.ConfigEntryController.ConsulServerConnMgr) + connMgrClient, err := consul.NewClientFromConnMgr(r.ConfigEntryController.ConsulClientConfig, r.ConfigEntryController.ConsulServerConnMgr) if err != nil { return err } - roles, _, err := client.ACL().RoleList(nil) + roles, _, err := connMgrClient.ACL().RoleList(nil) if err != nil { return err } @@ -189,7 +214,7 @@ func (r *TerminatingGatewayController) updateACls(log logr.Logger, termGW *consu return errors.New("terminating gateway role not found") } - terminatingGatewayRole, _, err := client.ACL().RoleRead(terminatingGatewayRoleID, nil) + terminatingGatewayRole, _, err := connMgrClient.ACL().RoleRead(terminatingGatewayRoleID, nil) if err != nil { return err } @@ -214,7 +239,7 @@ func (r *TerminatingGatewayController) updateACls(log logr.Logger, termGW *consu } if termGW.ObjectMeta.DeletionTimestamp.IsZero() { - termGWPoliciesToKeep, termGWPoliciesToRemove, err = r.handleModificationForPolicies(log, client, existingTermGWPolicies, termGW.Spec.Services) + termGWPoliciesToKeep, termGWPoliciesToRemove, err = r.handleModificationForPolicies(log, connMgrClient, existingTermGWPolicies, termGW.Spec.Services) if err != nil { return err } @@ -225,12 +250,12 @@ func (r *TerminatingGatewayController) updateACls(log logr.Logger, termGW *consu termGWPoliciesToKeep = append(termGWPoliciesToKeep, terminatingGatewayPolicy) terminatingGatewayRole.Policies = termGWPoliciesToKeep - _, _, err = client.ACL().RoleUpdate(terminatingGatewayRole, nil) + _, _, err = connMgrClient.ACL().RoleUpdate(terminatingGatewayRole, nil) if err != nil { return err } - err = r.conditionallyDeletePolicies(log, client, termGWPoliciesToRemove, termGW.Name) + err = r.conditionallyDeletePolicies(log, connMgrClient, termGWPoliciesToRemove, termGW.Name) if err != nil { return err } @@ -253,6 +278,7 @@ func (r *TerminatingGatewayController) handleModificationForPolicies(log logr.Lo termGWPoliciesToKeepNames := mapset.NewSet[string]() for _, service := range services { + log.Info("Checking for existing policies", "policy", servicePolicyName(service.Name, defaultIfEmpty(service.Namespace))) existingPolicy, _, err := client.ACL().PolicyReadByName(servicePolicyName(service.Name, defaultIfEmpty(service.Namespace)), &capi.QueryOptions{}) if err != nil { log.Error(err, "error reading policy") @@ -260,11 +286,17 @@ func (r *TerminatingGatewayController) handleModificationForPolicies(log logr.Lo } if existingPolicy == nil { + log.Info("No existing ACL Policies Found", "policy", servicePolicyName(service.Name, defaultIfEmpty(service.Namespace))) policyTemplate := getPolicyTemplateFor(service.Name) + policyNamespace := defaultIfEmpty(service.Namespace) + policyAdminPartition := r.adminPartition() + log.Info("Templating new ACL Policy", "Service", service.Name, "Namespace", policyNamespace, "Partition", policyAdminPartition) var data bytes.Buffer if err := policyTemplate.Execute(&data, templateArgs{ EnableNamespaces: r.NamespacesEnabled, - Namespace: defaultIfEmpty(service.Namespace), + EnablePartitions: r.PartitionsEnabled, + Namespace: policyNamespace, + Partition: policyAdminPartition, ServiceName: service.Name, }); err != nil { // just panic if we can't compile the simple template @@ -277,8 +309,13 @@ func (r *TerminatingGatewayController) handleModificationForPolicies(log logr.Lo Rules: data.String(), }, nil) if err != nil { + log.Error(err, "error creating policy") return nil, nil, err + } else { + log.Info("Created new ACL Policy", "Service", service.Name, "Namespace", policyNamespace, "Partition", policyAdminPartition) } + } else { + log.Info("Found for existing policies", "policy", existingPolicy.Name, "ID", existingPolicy.ID) } termGWPoliciesToKeep = append(termGWPoliciesToKeep, &capi.ACLRolePolicyLink{Name: servicePolicyName(service.Name, defaultIfEmpty(service.Namespace))}) diff --git a/control-plane/subcommand/inject-connect/v1controllers.go b/control-plane/subcommand/inject-connect/v1controllers.go index dd6b3be739..e5c3ae2b06 100644 --- a/control-plane/subcommand/inject-connect/v1controllers.go +++ b/control-plane/subcommand/inject-connect/v1controllers.go @@ -187,6 +187,7 @@ func (c *Command) configureControllers(ctx context.Context, mgr manager.Manager, ConsulDestinationNamespace: c.flagConsulDestinationNamespace, EnableNSMirroring: c.flagEnableK8SNSMirroring, NSMirroringPrefix: c.flagK8SNSMirroringPrefix, + ConsulPartition: c.consul.Partition, CrossNSACLPolicy: c.flagCrossNamespaceACLPolicy, } if err := (&controllers.ServiceDefaultsController{ @@ -275,6 +276,7 @@ func (c *Command) configureControllers(ctx context.Context, mgr manager.Manager, Client: mgr.GetClient(), Log: ctrl.Log.WithName("controller").WithName(apicommon.TerminatingGateway), NamespacesEnabled: c.flagEnableNamespaces, + PartitionsEnabled: c.flagEnablePartitions, Scheme: mgr.GetScheme(), }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", apicommon.TerminatingGateway)