Deploy service account for pod creation on demand
This commit is contained in:
		
							parent
							
								
									88c68712b6
								
							
						
					
					
						commit
						214ae04aa7
					
				|  | @ -256,6 +256,11 @@ func (c *Cluster) Create() error { | ||||||
| 	} | 	} | ||||||
| 	c.logger.Infof("pod disruption budget %q has been successfully created", util.NameFromMeta(pdb.ObjectMeta)) | 	c.logger.Infof("pod disruption budget %q has been successfully created", util.NameFromMeta(pdb.ObjectMeta)) | ||||||
| 
 | 
 | ||||||
|  | 	if err = c.syncPodServiceAccounts(); err != nil { | ||||||
|  | 		return fmt.Errorf("could not sync pod service accounts: %v", err) | ||||||
|  | 	} | ||||||
|  | 	c.logger.Infof("pod service accounts have been successfully synced") | ||||||
|  | 
 | ||||||
| 	if c.Statefulset != nil { | 	if c.Statefulset != nil { | ||||||
| 		return fmt.Errorf("statefulset already exists in the cluster") | 		return fmt.Errorf("statefulset already exists in the cluster") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -44,6 +44,12 @@ func (c *Cluster) Sync(newSpec *spec.Postgresql) (err error) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	c.logger.Debugf("syncing service accounts") | ||||||
|  | 	if err = c.syncPodServiceAccounts(); err != nil { | ||||||
|  | 		err = fmt.Errorf("could not sync service accounts: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	c.logger.Debugf("syncing services") | 	c.logger.Debugf("syncing services") | ||||||
| 	if err = c.syncServices(); err != nil { | 	if err = c.syncServices(); err != nil { | ||||||
| 		err = fmt.Errorf("could not sync services: %v", err) | 		err = fmt.Errorf("could not sync services: %v", err) | ||||||
|  | @ -103,6 +109,34 @@ func (c *Cluster) syncServices() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* | ||||||
|  |   Ensures the service account required by StatefulSets to create pods exists in all namespaces watched by the operator. | ||||||
|  | */ | ||||||
|  | func (c *Cluster) syncPodServiceAccounts() error { | ||||||
|  | 
 | ||||||
|  | 	podServiceAccount := c.Config.OpConfig.PodServiceAccountName | ||||||
|  | 	c.setProcessName("syncing pod service account in the watched namespaces") | ||||||
|  | 
 | ||||||
|  | 	_, err := c.KubeClient.ServiceAccounts(c.Namespace).Get(podServiceAccount, metav1.GetOptions{}) | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		c.logger.Warnf("the pod service account %q is absent from the namespace %q. Stateful sets in the namespace are unable to create pods.", podServiceAccount, c.Namespace) | ||||||
|  | 
 | ||||||
|  | 		c.OpConfig.PodServiceAccount.SetNamespace(c.Namespace) | ||||||
|  | 
 | ||||||
|  | 		_, err = c.KubeClient.ServiceAccounts(c.Namespace).Create(c.OpConfig.PodServiceAccount) | ||||||
|  | 		if err != nil { | ||||||
|  | 			c.logger.Warnf("cannot deploy the pod service account %q defined in the config map to the %q namespace: %v", podServiceAccount, c.Namespace, err) | ||||||
|  | 		} else { | ||||||
|  | 			c.logger.Infof("successfully deployed the pod service account %q to the %q namespace", podServiceAccount, c.Namespace) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		c.logger.Infof("successfully found the service account %q used to create pods to the namespace %q", podServiceAccount, c.Namespace) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *Cluster) syncService(role PostgresRole) error { | func (c *Cluster) syncService(role PostgresRole) error { | ||||||
| 	c.setProcessName("syncing %s service", role) | 	c.setProcessName("syncing %s service", role) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"github.com/Sirupsen/logrus" | 	"github.com/Sirupsen/logrus" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/types" | 	"k8s.io/apimachinery/pkg/types" | ||||||
|  | 	"k8s.io/client-go/kubernetes/scheme" | ||||||
| 	"k8s.io/client-go/pkg/api/v1" | 	"k8s.io/client-go/pkg/api/v1" | ||||||
| 	"k8s.io/client-go/tools/cache" | 	"k8s.io/client-go/tools/cache" | ||||||
| 
 | 
 | ||||||
|  | @ -113,11 +114,30 @@ func (c *Controller) initOperatorConfig() { | ||||||
| 	if scalyrAPIKey != "" { | 	if scalyrAPIKey != "" { | ||||||
| 		c.opConfig.ScalyrAPIKey = scalyrAPIKey | 		c.opConfig.ScalyrAPIKey = scalyrAPIKey | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Controller) initPodServiceAccount() { | ||||||
|  | 	// re-uses k8s internal parsing. See k8s client-go issue #193 for explanation
 | ||||||
|  | 	decode := scheme.Codecs.UniversalDeserializer().Decode | ||||||
|  | 	obj, groupVersionKind, err := decode([]byte(c.opConfig.PodServiceAccountDefinition), nil, nil) | ||||||
|  | 
 | ||||||
|  | 	switch { | ||||||
|  | 	case err != nil: | ||||||
|  | 		panic(fmt.Errorf("Unable to parse pod service account definiton from the operator config map: %v", err)) | ||||||
|  | 	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) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// actual service accounts are deployed lazily at the time of cluster creation or sync
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Controller) initController() { | func (c *Controller) initController() { | ||||||
| 	c.initClients() | 	c.initClients() | ||||||
| 	c.initOperatorConfig() | 	c.initOperatorConfig() | ||||||
|  | 	c.initPodServiceAccount() | ||||||
| 
 | 
 | ||||||
| 	c.initSharedInformers() | 	c.initSharedInformers() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"github.com/zalando-incubator/postgres-operator/pkg/spec" | 	"github.com/zalando-incubator/postgres-operator/pkg/spec" | ||||||
|  | 	"k8s.io/client-go/pkg/api/v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // CRD describes CustomResourceDefinition specific configuration parameters
 | // CRD describes CustomResourceDefinition specific configuration parameters
 | ||||||
|  | @ -67,21 +68,25 @@ type Config struct { | ||||||
| 	Resources | 	Resources | ||||||
| 	Auth | 	Auth | ||||||
| 	Scalyr | 	Scalyr | ||||||
| 	WatchedNamespace          string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
 | 	PodServiceAccount *v1.ServiceAccount | ||||||
| 	EtcdHost                  string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` | 	WatchedNamespace  string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
 | ||||||
| 	DockerImage               string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` | 	EtcdHost          string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` | ||||||
| 	ServiceAccountName        string `name:"service_account_name" default:"operator"` | 	DockerImage       string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` | ||||||
| 	DbHostedZone              string `name:"db_hosted_zone" default:"db.example.com"` | 	// re-use one account for both Spilo pods and the operator; this grants extra privileges to pods
 | ||||||
| 	EtcdScope                 string `name:"etcd_scope" default:"service"` | 	ServiceAccountName          string `name:"service_account_name" default:"operator"` | ||||||
| 	WALES3Bucket              string `name:"wal_s3_bucket"` | 	PodServiceAccountName       string `name:"pod_service_account_name" default:"operator"` | ||||||
| 	KubeIAMRole               string `name:"kube_iam_role"` | 	PodServiceAccountDefinition string `name:"pod_service_account_definition" default:"apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: operator\n"` | ||||||
| 	DebugLogging              bool   `name:"debug_logging" default:"true"` | 	DbHostedZone                string `name:"db_hosted_zone" default:"db.example.com"` | ||||||
| 	EnableDBAccess            bool   `name:"enable_database_access" default:"true"` | 	EtcdScope                   string `name:"etcd_scope" default:"service"` | ||||||
| 	EnableTeamsAPI            bool   `name:"enable_teams_api" default:"true"` | 	WALES3Bucket                string `name:"wal_s3_bucket"` | ||||||
| 	EnableTeamSuperuser       bool   `name:"enable_team_superuser" default:"false"` | 	KubeIAMRole                 string `name:"kube_iam_role"` | ||||||
| 	TeamAdminRole             string `name:"team_admin_role" default:"admin"` | 	DebugLogging                bool   `name:"debug_logging" default:"true"` | ||||||
| 	EnableMasterLoadBalancer  bool   `name:"enable_master_load_balancer" default:"true"` | 	EnableDBAccess              bool   `name:"enable_database_access" default:"true"` | ||||||
| 	EnableReplicaLoadBalancer bool   `name:"enable_replica_load_balancer" default:"false"` | 	EnableTeamsAPI              bool   `name:"enable_teams_api" default:"true"` | ||||||
|  | 	EnableTeamSuperuser         bool   `name:"enable_team_superuser" default:"false"` | ||||||
|  | 	TeamAdminRole               string `name:"team_admin_role" default:"admin"` | ||||||
|  | 	EnableMasterLoadBalancer    bool   `name:"enable_master_load_balancer" default:"true"` | ||||||
|  | 	EnableReplicaLoadBalancer   bool   `name:"enable_replica_load_balancer" default:"false"` | ||||||
| 	// deprecated and kept for backward compatibility
 | 	// deprecated and kept for backward compatibility
 | ||||||
| 	EnableLoadBalancer       *bool             `name:"enable_load_balancer" default:"true"` | 	EnableLoadBalancer       *bool             `name:"enable_load_balancer" default:"true"` | ||||||
| 	MasterDNSNameFormat      stringTemplate    `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` | 	MasterDNSNameFormat      stringTemplate    `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue