diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 661fe5399..6e2999290 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -42,6 +42,7 @@ type Config struct { OpConfig config.Config RestConfig *rest.Config InfrastructureRoles map[string]spec.PgUser // inherited from the controller + PodServiceAccount *v1.ServiceAccount } type kubeResources struct { @@ -209,11 +210,12 @@ func (c *Cluster) createPodServiceAccounts() error { if err != nil { c.logger.Warnf("the pod service account %q cannot be retrieved in the namespace %q. Stateful sets in the namespace may be unable to create pods. Error: %v", podServiceAccountName, c.Namespace, err) - // when created, each Cluster struct gets a separate copy of OpConfig - // including the nested PodServiceAccount struct, so no race condition here - c.OpConfig.PodServiceAccount.SetNamespace(c.Namespace) + // get a separate copy of service account + // to prevent a race condition when setting a namespace for many clusters + sa := *c.PodServiceAccount + sa.SetNamespace(c.Namespace) - _, err = c.KubeClient.ServiceAccounts(c.Namespace).Create(&c.OpConfig.PodServiceAccount) + _, err = c.KubeClient.ServiceAccounts(c.Namespace).Create(&sa) if err != nil { return fmt.Errorf("cannot deploy the pod service account %q defined in the config map to the %q namespace: %v", podServiceAccountName, c.Namespace, err) } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index ab22f1359..2a9c26d8b 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -51,6 +51,8 @@ type Controller struct { lastClusterSyncTime int64 workerLogs map[uint32]ringlog.RingLogger + + PodServiceAccount *v1.ServiceAccount } // NewController creates a new controller @@ -128,9 +130,9 @@ func (c *Controller) initPodServiceAccount() { case groupVersionKind.Kind != "ServiceAccount": panic(fmt.Errorf("pod service account definiton in the operator config map defines another type of resource: %v", groupVersionKind.Kind)) default: - c.opConfig.PodServiceAccount = *obj.(*v1.ServiceAccount) + c.PodServiceAccount = obj.(*v1.ServiceAccount) // ensure consistent naming of the account - c.opConfig.PodServiceAccount.Name = c.opConfig.PodServiceAccountName + c.PodServiceAccount.Name = c.opConfig.PodServiceAccountName } // actual service accounts are deployed at the time of Postgres/Spilo cluster creation diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 69124c111..5e46e93eb 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -26,6 +26,7 @@ func (c *Controller) makeClusterConfig() cluster.Config { RestConfig: c.config.RestConfig, OpConfig: config.Copy(c.opConfig), InfrastructureRoles: infrastructureRoles, + PodServiceAccount: c.PodServiceAccount, } } diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index bb810665b..72731442c 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/zalando-incubator/postgres-operator/pkg/spec" - "k8s.io/client-go/pkg/api/v1" ) // CRD describes CustomResourceDefinition specific configuration parameters @@ -68,10 +67,10 @@ type Config struct { Resources Auth Scalyr - PodServiceAccount v1.ServiceAccount // has to be struct value, not a pointer - WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' - EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` - DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` + + WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' + EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` + DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` // re-use one account for both Spilo pods and the operator; this grants extra privileges to pods PodServiceAccountName string `name:"pod_service_account_name" default:"operator"` PodServiceAccountDefinition string `name:"pod_service_account_definition" default:"apiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: operator\n"`