From ea0ed11e86b547db7519127803905a1e980736a8 Mon Sep 17 00:00:00 2001
From: Murat Kabilov <murat@kabilov.com>
Date: Thu, 13 Jul 2017 16:51:41 +0200
Subject: [PATCH] move cluster config struct to the spec package

---
 pkg/cluster/cluster.go       | 27 ++++++++-------------------
 pkg/cluster/k8sres.go        | 32 +++++++++++++++-----------------
 pkg/cluster/pg.go            |  4 ++--
 pkg/cluster/resources.go     |  4 ++--
 pkg/cluster/util.go          | 36 ++++++++++++++++++------------------
 pkg/controller/postgresql.go |  5 +++--
 pkg/controller/util.go       |  7 ++-----
 pkg/spec/types.go            | 13 +++++++++++++
 8 files changed, 63 insertions(+), 65 deletions(-)

diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go
index 1dbb15df..03cfca51 100644
--- a/pkg/cluster/cluster.go
+++ b/pkg/cluster/cluster.go
@@ -11,12 +11,10 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/pkg/api"
 	"k8s.io/client-go/pkg/api/v1"
 	"k8s.io/client-go/pkg/apis/apps/v1beta1"
 	"k8s.io/client-go/pkg/types"
-	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/cache"
 
 	"github.com/zalando-incubator/postgres-operator/pkg/spec"
@@ -24,7 +22,6 @@ import (
 	"github.com/zalando-incubator/postgres-operator/pkg/util/config"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
-	"github.com/zalando-incubator/postgres-operator/pkg/util/teams"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/users"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/volumes"
 )
@@ -34,16 +31,6 @@ var (
 	userRegexp         = regexp.MustCompile(`^[a-z0-9]([-_a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-_a-z0-9]*[a-z0-9])?)*$`)
 )
 
-// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
-type Config struct {
-	KubeClient          *kubernetes.Clientset //TODO: move clients to the better place?
-	RestClient          *rest.RESTClient
-	RestConfig          *rest.Config
-	TeamsAPIClient      *teams.API
-	OpConfig            config.Config
-	InfrastructureRoles map[string]spec.PgUser // inherited from the controller
-}
-
 type kubeResources struct {
 	Service     map[PostgresRole]*v1.Service
 	Endpoint    *v1.Endpoints
@@ -56,7 +43,8 @@ type kubeResources struct {
 type Cluster struct {
 	kubeResources
 	spec.Postgresql
-	Config
+	spec.ClusterConfig
+	config.Config
 	logger           *logrus.Entry
 	pgUsers          map[string]spec.PgUser
 	systemUsers      map[string]spec.PgUser
@@ -78,7 +66,7 @@ type compareStatefulsetResult struct {
 }
 
 // New creates a new cluster. This function should be called from a controller.
-func New(cfg Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
+func New(cfg spec.ClusterConfig, opCfg config.Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
 	lg := logger.WithField("pkg", "cluster").WithField("cluster-name", pgSpec.Metadata.Name)
 	kubeResources := kubeResources{Secrets: make(map[types.UID]*v1.Secret), Service: make(map[PostgresRole]*v1.Service)}
 	orphanDependents := true
@@ -93,7 +81,8 @@ func New(cfg Config, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
 	})
 
 	cluster := &Cluster{
-		Config:           cfg,
+		ClusterConfig:    cfg,
+		Config:           opCfg,
 		Postgresql:       pgSpec,
 		logger:           lg,
 		pgUsers:          make(map[string]spec.PgUser),
@@ -586,11 +575,11 @@ func (c *Cluster) initSystemUsers() {
 	// secrets, therefore, setting flags like SUPERUSER or REPLICATION
 	// is not necessary here
 	c.systemUsers[constants.SuperuserKeyName] = spec.PgUser{
-		Name:     c.OpConfig.SuperUsername,
+		Name:     c.SuperUsername,
 		Password: util.RandomPassword(constants.PasswordLength),
 	}
 	c.systemUsers[constants.ReplicationUserKeyName] = spec.PgUser{
-		Name:     c.OpConfig.ReplicationUsername,
+		Name:     c.ReplicationUsername,
 		Password: util.RandomPassword(constants.PasswordLength),
 	}
 }
@@ -623,7 +612,7 @@ func (c *Cluster) initHumanUsers() error {
 	}
 	for _, username := range teamMembers {
 		flags := []string{constants.RoleFlagLogin, constants.RoleFlagSuperuser}
-		memberOf := []string{c.OpConfig.PamRoleName}
+		memberOf := []string{c.PamRoleName}
 		c.pgUsers[username] = spec.PgUser{Name: username, Flags: flags, MemberOf: memberOf}
 	}
 
diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go
index fd71661a..77843110 100644
--- a/pkg/cluster/k8sres.go
+++ b/pkg/cluster/k8sres.go
@@ -50,10 +50,8 @@ func (c *Cluster) resourceRequirements(resources spec.Resources) (*v1.ResourceRe
 	specRequests := resources.ResourceRequest
 	specLimits := resources.ResourceLimits
 
-	config := c.OpConfig
-
-	defaultRequests := spec.ResourceDescription{CPU: config.DefaultCPURequest, Memory: config.DefaultMemoryRequest}
-	defaultLimits := spec.ResourceDescription{CPU: config.DefaultCPULimit, Memory: config.DefaultMemoryLimit}
+	defaultRequests := spec.ResourceDescription{CPU: c.DefaultCPURequest, Memory: c.DefaultMemoryRequest}
+	defaultLimits := spec.ResourceDescription{CPU: c.DefaultCPULimit, Memory: c.DefaultMemoryLimit}
 
 	result := v1.ResourceRequirements{}
 
@@ -166,7 +164,7 @@ PATRONI_INITDB_PARAMS:
 	} else {
 		config.Bootstrap.PgHBA = []string{
 			"hostnossl all all all reject",
-			fmt.Sprintf("hostssl   all +%s all pam", c.OpConfig.PamRoleName),
+			fmt.Sprintf("hostssl   all +%s all pam", c.PamRoleName),
 			"hostssl   all all all md5",
 		}
 	}
@@ -190,7 +188,7 @@ PATRONI_INITDB_PARAMS:
 		config.PgLocalConfiguration[patroniPGParametersParameterName] = pg.Parameters
 	}
 	config.Bootstrap.Users = map[string]pgUser{
-		c.OpConfig.PamRoleName: {
+		c.PamRoleName: {
 			Password: "",
 			Options:  []string{constants.RoleFlagCreateDB, constants.RoleFlagNoLogin},
 		},
@@ -217,7 +215,7 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 		},
 		{
 			Name:  "ETCD_HOST",
-			Value: c.OpConfig.EtcdHost,
+			Value: c.EtcdHost,
 		},
 		{
 			Name: "POD_IP",
@@ -242,7 +240,7 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 			ValueFrom: &v1.EnvVarSource{
 				SecretKeyRef: &v1.SecretKeySelector{
 					LocalObjectReference: v1.LocalObjectReference{
-						Name: c.credentialSecretName(c.OpConfig.SuperUsername),
+						Name: c.credentialSecretName(c.SuperUsername),
 					},
 					Key: "password",
 				},
@@ -253,7 +251,7 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 			ValueFrom: &v1.EnvVarSource{
 				SecretKeyRef: &v1.SecretKeySelector{
 					LocalObjectReference: v1.LocalObjectReference{
-						Name: c.credentialSecretName(c.OpConfig.ReplicationUsername),
+						Name: c.credentialSecretName(c.ReplicationUsername),
 					},
 					Key: "password",
 				},
@@ -261,19 +259,19 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 		},
 		{
 			Name:  "PAM_OAUTH2",
-			Value: c.OpConfig.PamConfiguration,
+			Value: c.PamConfiguration,
 		},
 	}
 	if spiloConfiguration != "" {
 		envVars = append(envVars, v1.EnvVar{Name: "SPILO_CONFIGURATION", Value: spiloConfiguration})
 	}
-	if c.OpConfig.WALES3Bucket != "" {
-		envVars = append(envVars, v1.EnvVar{Name: "WAL_S3_BUCKET", Value: c.OpConfig.WALES3Bucket})
+	if c.WALES3Bucket != "" {
+		envVars = append(envVars, v1.EnvVar{Name: "WAL_S3_BUCKET", Value: c.WALES3Bucket})
 	}
 	privilegedMode := bool(true)
 	container := v1.Container{
 		Name:            c.Metadata.Name,
-		Image:           c.OpConfig.DockerImage,
+		Image:           c.DockerImage,
 		ImagePullPolicy: v1.PullAlways,
 		Resources:       *resourceRequirements,
 		Ports: []v1.ContainerPort{
@@ -304,7 +302,7 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 	terminateGracePeriodSeconds := int64(30)
 
 	podSpec := v1.PodSpec{
-		ServiceAccountName:            c.OpConfig.ServiceAccountName,
+		ServiceAccountName:            c.ServiceAccountName,
 		TerminationGracePeriodSeconds: &terminateGracePeriodSeconds,
 		Containers:                    []v1.Container{container},
 	}
@@ -316,8 +314,8 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements,
 		},
 		Spec: podSpec,
 	}
-	if c.OpConfig.KubeIAMRole != "" {
-		template.Annotations = map[string]string{constants.KubeIAmAnnotation: c.OpConfig.KubeIAMRole}
+	if c.KubeIAMRole != "" {
+		template.Annotations = map[string]string{constants.KubeIAmAnnotation: c.KubeIAMRole}
 	}
 
 	return &template
@@ -451,7 +449,7 @@ func (c *Cluster) genService(role PostgresRole, allowedSourceRanges []string) *v
 		},
 	}
 	if role == Replica {
-		service.Spec.Selector = map[string]string{c.OpConfig.PodRoleLabel: string(Replica)}
+		service.Spec.Selector = map[string]string{c.PodRoleLabel: string(Replica)}
 	}
 
 	return service
diff --git a/pkg/cluster/pg.go b/pkg/cluster/pg.go
index fd677006..93cd9e5a 100644
--- a/pkg/cluster/pg.go
+++ b/pkg/cluster/pg.go
@@ -33,11 +33,11 @@ func (c *Cluster) pgConnectionString() string {
 }
 
 func (c *Cluster) databaseAccessDisabled() bool {
-	if !c.OpConfig.EnableDBAccess {
+	if !c.EnableDBAccess {
 		c.logger.Debugf("Database access is disabled")
 	}
 
-	return !c.OpConfig.EnableDBAccess
+	return !c.EnableDBAccess
 }
 
 func (c *Cluster) initDbConn() (err error) {
diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go
index aa596430..7069bf90 100644
--- a/pkg/cluster/resources.go
+++ b/pkg/cluster/resources.go
@@ -28,7 +28,7 @@ func (c *Cluster) loadResources() error {
 		return fmt.Errorf("too many(%d) services for a cluster", len(services.Items))
 	}
 	for i, svc := range services.Items {
-		switch PostgresRole(svc.Labels[c.OpConfig.PodRoleLabel]) {
+		switch PostgresRole(svc.Labels[c.PodRoleLabel]) {
 		case Replica:
 			c.Service[Replica] = &services.Items[i]
 		default:
@@ -45,7 +45,7 @@ func (c *Cluster) loadResources() error {
 	}
 
 	for i, ep := range endpoints.Items {
-		if ep.Labels[c.OpConfig.PodRoleLabel] != string(Replica) {
+		if ep.Labels[c.PodRoleLabel] != string(Replica) {
 			c.Endpoint = &endpoints.Items[i]
 			break
 		}
diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go
index b6416cd7..41ebd3a6 100644
--- a/pkg/cluster/util.go
+++ b/pkg/cluster/util.go
@@ -111,11 +111,11 @@ func (c *Cluster) getOAuthToken() (string, error) {
 	//TODO: we can move this function to the Controller in case it will be needed there. As for now we use it only in the Cluster
 	// Temporary getting postgresql-operator secret from the NamespaceDefault
 	credentialsSecret, err := c.KubeClient.
-		Secrets(c.OpConfig.OAuthTokenSecretName.Namespace).
-		Get(c.OpConfig.OAuthTokenSecretName.Name)
+		Secrets(c.OAuthTokenSecretName.Namespace).
+		Get(c.OAuthTokenSecretName.Name)
 
 	if err != nil {
-		c.logger.Debugf("Oauth token secret name: %s", c.OpConfig.OAuthTokenSecretName)
+		c.logger.Debugf("Oauth token secret name: %s", c.OAuthTokenSecretName)
 		return "", fmt.Errorf("could not get credentials secret: %v", err)
 	}
 	data := credentialsSecret.Data
@@ -131,7 +131,7 @@ func (c *Cluster) getTeamMembers() ([]string, error) {
 	if c.Spec.TeamID == "" {
 		return nil, fmt.Errorf("no teamId specified")
 	}
-	if !c.OpConfig.EnableTeamsAPI {
+	if !c.EnableTeamsAPI {
 		c.logger.Debug("Team API is disabled, returning empty list of members")
 		return []string{}, nil
 	}
@@ -160,7 +160,7 @@ func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent) error {
 			if role == constants.PodRoleMaster || role == constants.PodRoleReplica {
 				return nil
 			}
-		case <-time.After(c.OpConfig.PodLabelWaitTimeout):
+		case <-time.After(c.PodLabelWaitTimeout):
 			return fmt.Errorf("pod label wait timeout")
 		}
 	}
@@ -173,14 +173,14 @@ func (c *Cluster) waitForPodDeletion(podEvents chan spec.PodEvent) error {
 			if podEvent.EventType == spec.EventDelete {
 				return nil
 			}
-		case <-time.After(c.OpConfig.PodDeletionWaitTimeout):
+		case <-time.After(c.PodDeletionWaitTimeout):
 			return fmt.Errorf("pod deletion wait timeout")
 		}
 	}
 }
 
 func (c *Cluster) waitStatefulsetReady() error {
-	return retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
+	return retryutil.Retry(c.ResourceCheckInterval, c.ResourceCheckTimeout,
 		func() (bool, error) {
 			listOptions := v1.ListOptions{
 				LabelSelector: c.labelsSet().String(),
@@ -207,12 +207,12 @@ func (c *Cluster) waitPodLabelsReady() error {
 	}
 	masterListOption := v1.ListOptions{
 		LabelSelector: labels.Merge(ls, labels.Set{
-			c.OpConfig.PodRoleLabel: constants.PodRoleMaster,
+			c.PodRoleLabel: constants.PodRoleMaster,
 		}).String(),
 	}
 	replicaListOption := v1.ListOptions{
 		LabelSelector: labels.Merge(ls, labels.Set{
-			c.OpConfig.PodRoleLabel: constants.PodRoleReplica,
+			c.PodRoleLabel: constants.PodRoleReplica,
 		}).String(),
 	}
 	pods, err := c.KubeClient.Pods(namespace).List(listOptions)
@@ -221,7 +221,7 @@ func (c *Cluster) waitPodLabelsReady() error {
 	}
 	podsNumber := len(pods.Items)
 
-	err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
+	err = retryutil.Retry(c.ResourceCheckInterval, c.ResourceCheckTimeout,
 		func() (bool, error) {
 			masterPods, err := c.KubeClient.Pods(namespace).List(masterListOption)
 			if err != nil {
@@ -263,32 +263,32 @@ func (c *Cluster) waitStatefulsetPodsReady() error {
 
 func (c *Cluster) labelsSet() labels.Set {
 	lbls := make(map[string]string)
-	for k, v := range c.OpConfig.ClusterLabels {
+	for k, v := range c.ClusterLabels {
 		lbls[k] = v
 	}
-	lbls[c.OpConfig.ClusterNameLabel] = c.Metadata.Name
+	lbls[c.ClusterNameLabel] = c.Metadata.Name
 
 	return labels.Set(lbls)
 }
 
 func (c *Cluster) roleLabelsSet(role PostgresRole) labels.Set {
 	lbls := c.labelsSet()
-	lbls[c.OpConfig.PodRoleLabel] = string(role)
+	lbls[c.PodRoleLabel] = string(role)
 	return lbls
 }
 
 func (c *Cluster) masterDnsName() string {
-	return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format(
+	return strings.ToLower(c.MasterDNSNameFormat.Format(
 		"cluster", c.Spec.ClusterName,
 		"team", c.teamName(),
-		"hostedzone", c.OpConfig.DbHostedZone))
+		"hostedzone", c.DbHostedZone))
 }
 
 func (c *Cluster) replicaDnsName() string {
-	return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format(
+	return strings.ToLower(c.ReplicaDNSNameFormat.Format(
 		"cluster", c.Spec.ClusterName,
 		"team", c.teamName(),
-		"hostedzone", c.OpConfig.DbHostedZone))
+		"hostedzone", c.DbHostedZone))
 }
 
 func (c *Cluster) credentialSecretName(username string) string {
@@ -300,5 +300,5 @@ func (c *Cluster) credentialSecretName(username string) string {
 }
 
 func (c *Cluster) podSpiloRole(pod *v1.Pod) string {
-	return pod.Labels[c.OpConfig.PodRoleLabel]
+	return pod.Labels[c.PodRoleLabel]
 }
diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go
index 06a334e7..895a25ae 100644
--- a/pkg/controller/postgresql.go
+++ b/pkg/controller/postgresql.go
@@ -17,6 +17,7 @@ import (
 	"github.com/zalando-incubator/postgres-operator/pkg/cluster"
 	"github.com/zalando-incubator/postgres-operator/pkg/spec"
 	"github.com/zalando-incubator/postgres-operator/pkg/util"
+	"github.com/zalando-incubator/postgres-operator/pkg/util/config"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
 )
 
@@ -125,7 +126,7 @@ func (c *Controller) processEvent(obj interface{}) error {
 		logger.Infof("Creation of the '%s' cluster started", clusterName)
 
 		stopCh := make(chan struct{})
-		cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger)
+		cl = cluster.New(c.makeClusterConfig(), config.Copy(c.opConfig), *event.NewSpec, logger)
 		cl.Run(stopCh)
 
 		c.clustersMu.Lock()
@@ -176,7 +177,7 @@ func (c *Controller) processEvent(obj interface{}) error {
 		// no race condition because a cluster is always processed by single worker
 		if !clusterFound {
 			stopCh := make(chan struct{})
-			cl = cluster.New(c.makeClusterConfig(), *event.NewSpec, logger)
+			cl = cluster.New(c.makeClusterConfig(), config.Copy(c.opConfig), *event.NewSpec, logger)
 			cl.Run(stopCh)
 
 			c.clustersMu.Lock()
diff --git a/pkg/controller/util.go b/pkg/controller/util.go
index 76b33168..c60edef4 100644
--- a/pkg/controller/util.go
+++ b/pkg/controller/util.go
@@ -7,25 +7,22 @@ import (
 	"k8s.io/client-go/pkg/api/v1"
 	extv1beta "k8s.io/client-go/pkg/apis/extensions/v1beta1"
 
-	"github.com/zalando-incubator/postgres-operator/pkg/cluster"
 	"github.com/zalando-incubator/postgres-operator/pkg/spec"
-	"github.com/zalando-incubator/postgres-operator/pkg/util/config"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
 	"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
 )
 
-func (c *Controller) makeClusterConfig() cluster.Config {
+func (c *Controller) makeClusterConfig() spec.ClusterConfig {
 	infrastructureRoles := make(map[string]spec.PgUser)
 	for k, v := range c.InfrastructureRoles {
 		infrastructureRoles[k] = v
 	}
 
-	return cluster.Config{
+	return spec.ClusterConfig{
 		KubeClient:          c.KubeClient,
 		RestClient:          c.RestClient,
 		RestConfig:          c.RestConfig,
 		TeamsAPIClient:      c.TeamsAPIClient,
-		OpConfig:            config.Copy(c.opConfig),
 		InfrastructureRoles: infrastructureRoles,
 	}
 }
diff --git a/pkg/spec/types.go b/pkg/spec/types.go
index c95a3e71..7316f46f 100644
--- a/pkg/spec/types.go
+++ b/pkg/spec/types.go
@@ -5,8 +5,12 @@ import (
 	"fmt"
 	"strings"
 
+	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/pkg/api/v1"
 	"k8s.io/client-go/pkg/types"
+	"k8s.io/client-go/rest"
+
+	"github.com/zalando-incubator/postgres-operator/pkg/util/teams"
 )
 
 // EvenType contains type of the events for the TPRs and Pods received from Kubernetes
@@ -67,6 +71,15 @@ type PgSyncUserRequest struct {
 	User PgUser
 }
 
+// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
+type ClusterConfig struct {
+	KubeClient          *kubernetes.Clientset //TODO: move clients to the better place?
+	RestClient          *rest.RESTClient
+	TeamsAPIClient      *teams.API
+	RestConfig          *rest.Config
+	InfrastructureRoles map[string]PgUser // inherited from the controller
+}
+
 // UserSyncer defines an interface for the implementations to sync users from the manifest to the DB.
 type UserSyncer interface {
 	ProduceSyncRequests(dbUsers PgUserMap, newUsers PgUserMap) (req []PgSyncUserRequest)
-- 
GitLab