Add a simple tests for CreateSecret
This commit is contained in:
		
							parent
							
								
									ba1f886043
								
							
						
					
					
						commit
						b0756a1f75
					
				|  | @ -127,6 +127,14 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres | ||||||
| 	return cluster | 	return cluster | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *Cluster) SetTeamsAPIClient(client teams.Interface) { | ||||||
|  | 	c.teamsAPIClient = client | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) SetOAuthTokenGetter(getter OAuthTokenGetter) { | ||||||
|  | 	c.oauthTokenGetter = getter | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *Cluster) clusterName() spec.NamespacedName { | func (c *Cluster) clusterName() spec.NamespacedName { | ||||||
| 	return util.NameFromMeta(c.ObjectMeta) | 	return util.NameFromMeta(c.ObjectMeta) | ||||||
| } | } | ||||||
|  | @ -222,7 +230,7 @@ func (c *Cluster) PlanForSecrets() (plan Plan) { | ||||||
| 
 | 
 | ||||||
| 		if k8sutil.ResourceNotFound(err) { | 		if k8sutil.ResourceNotFound(err) { | ||||||
| 			msg = "Generate plan to create new secret %q" | 			msg = "Generate plan to create new secret %q" | ||||||
| 			c.logger.Debugf(msg, util.NameFromMeta(secret.ObjectMeta)) | 			c.logger.Debugf(msg, util.NameFromMeta(secretSpec.ObjectMeta)) | ||||||
| 			plan = append(plan, NewCreateSecret(secretUsername, secretSpec, c)) | 			plan = append(plan, NewCreateSecret(secretUsername, secretSpec, c)) | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ import ( | ||||||
| 	"k8s.io/client-go/kubernetes/scheme" | 	"k8s.io/client-go/kubernetes/scheme" | ||||||
| 	"k8s.io/client-go/tools/cache" | 	"k8s.io/client-go/tools/cache" | ||||||
| 
 | 
 | ||||||
|  | 	acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" | ||||||
| 	"github.com/zalando-incubator/postgres-operator/pkg/apiserver" | 	"github.com/zalando-incubator/postgres-operator/pkg/apiserver" | ||||||
| 	"github.com/zalando-incubator/postgres-operator/pkg/cluster" | 	"github.com/zalando-incubator/postgres-operator/pkg/cluster" | ||||||
| 	"github.com/zalando-incubator/postgres-operator/pkg/spec" | 	"github.com/zalando-incubator/postgres-operator/pkg/spec" | ||||||
|  | @ -25,6 +26,20 @@ import ( | ||||||
| 	acidv1informer "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do/v1" | 	acidv1informer "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do/v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type ClusterFactory interface { | ||||||
|  | 	addCluster(ctrl *Controller, | ||||||
|  | 		lg *logrus.Entry, | ||||||
|  | 		clusterName spec.NamespacedName, | ||||||
|  | 		pgSpec *acidv1.Postgresql, | ||||||
|  | 	) *cluster.Cluster | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ClusterGenerator struct { | ||||||
|  | 	ClusterFactory | ||||||
|  | 
 | ||||||
|  | 	KubeClient k8sutil.KubernetesClient | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Controller represents operator controller
 | // Controller represents operator controller
 | ||||||
| type Controller struct { | type Controller struct { | ||||||
| 	config   spec.ControllerConfig | 	config   spec.ControllerConfig | ||||||
|  | @ -59,6 +74,8 @@ type Controller struct { | ||||||
| 	PodServiceAccount            *v1.ServiceAccount | 	PodServiceAccount            *v1.ServiceAccount | ||||||
| 	PodServiceAccountRoleBinding *rbacv1beta1.RoleBinding | 	PodServiceAccountRoleBinding *rbacv1beta1.RoleBinding | ||||||
| 	namespacesWithDefinedRBAC    sync.Map | 	namespacesWithDefinedRBAC    sync.Map | ||||||
|  | 
 | ||||||
|  | 	clusterFactory ClusterFactory | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewController creates a new controller
 | // NewController creates a new controller
 | ||||||
|  | @ -77,6 +94,7 @@ func NewController(controllerConfig *spec.ControllerConfig) *Controller { | ||||||
| 		teamClusters:     make(map[string][]spec.NamespacedName), | 		teamClusters:     make(map[string][]spec.NamespacedName), | ||||||
| 		stopCh:           make(chan struct{}), | 		stopCh:           make(chan struct{}), | ||||||
| 		podCh:            make(chan cluster.PodEvent), | 		podCh:            make(chan cluster.PodEvent), | ||||||
|  | 		clusterFactory:   &ClusterGenerator{}, | ||||||
| 	} | 	} | ||||||
| 	logger.Hooks.Add(c) | 	logger.Hooks.Add(c) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -136,7 +136,7 @@ func (c *Controller) acquireInitialListOfClusters() error { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		clusterName = util.NameFromMeta(pg.ObjectMeta) | 		clusterName = util.NameFromMeta(pg.ObjectMeta) | ||||||
| 		c.addCluster(c.logger, clusterName, &pg) | 		c.clusterFactory.addCluster(c, c.logger, clusterName, &pg) | ||||||
| 		c.logger.Debugf("added new cluster: %q", clusterName) | 		c.logger.Debugf("added new cluster: %q", clusterName) | ||||||
| 	} | 	} | ||||||
| 	// initiate initial sync of all clusters.
 | 	// initiate initial sync of all clusters.
 | ||||||
|  | @ -144,18 +144,20 @@ func (c *Controller) acquireInitialListOfClusters() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { | func (c *ClusterGenerator) addCluster(ctrl *Controller, | ||||||
| 	cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg) | 	lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { | ||||||
| 	cl.Run(c.stopCh) | 
 | ||||||
|  | 	cl := cluster.New(ctrl.makeClusterConfig(), ctrl.KubeClient, *pgSpec, lg) | ||||||
|  | 	cl.Run(ctrl.stopCh) | ||||||
| 	teamName := strings.ToLower(cl.Spec.TeamID) | 	teamName := strings.ToLower(cl.Spec.TeamID) | ||||||
| 
 | 
 | ||||||
| 	defer c.clustersMu.Unlock() | 	defer ctrl.clustersMu.Unlock() | ||||||
| 	c.clustersMu.Lock() | 	ctrl.clustersMu.Lock() | ||||||
| 
 | 
 | ||||||
| 	c.teamClusters[teamName] = append(c.teamClusters[teamName], clusterName) | 	ctrl.teamClusters[teamName] = append(ctrl.teamClusters[teamName], clusterName) | ||||||
| 	c.clusters[clusterName] = cl | 	ctrl.clusters[clusterName] = cl | ||||||
| 	c.clusterLogs[clusterName] = ringlog.New(c.opConfig.RingLogLines) | 	ctrl.clusterLogs[clusterName] = ringlog.New(ctrl.opConfig.RingLogLines) | ||||||
| 	c.clusterHistory[clusterName] = ringlog.New(c.opConfig.ClusterHistoryEntries) | 	ctrl.clusterHistory[clusterName] = ringlog.New(ctrl.opConfig.ClusterHistoryEntries) | ||||||
| 
 | 
 | ||||||
| 	return cl | 	return cl | ||||||
| } | } | ||||||
|  | @ -182,7 +184,7 @@ func (c *Controller) generatePlan(event ClusterEvent) cluster.Plan { | ||||||
| 	case EventAdd: | 	case EventAdd: | ||||||
| 		log.Infof("Creation of the cluster started") | 		log.Infof("Creation of the cluster started") | ||||||
| 
 | 
 | ||||||
| 		newCluster := c.addCluster(log, clusterName, event.NewSpec) | 		newCluster := c.clusterFactory.addCluster(c, log, clusterName, event.NewSpec) | ||||||
| 		c.curWorkerCluster.Store(event.WorkerID, newCluster) | 		c.curWorkerCluster.Store(event.WorkerID, newCluster) | ||||||
| 		return newCluster.PlanForCreate() | 		return newCluster.PlanForCreate() | ||||||
| 
 | 
 | ||||||
|  | @ -272,7 +274,7 @@ func (c *Controller) processEvent(event ClusterEvent) { | ||||||
| 
 | 
 | ||||||
| 		lg.Infof("creation of the cluster started") | 		lg.Infof("creation of the cluster started") | ||||||
| 
 | 
 | ||||||
| 		cl = c.addCluster(lg, clusterName, event.NewSpec) | 		cl = c.clusterFactory.addCluster(c, lg, clusterName, event.NewSpec) | ||||||
| 
 | 
 | ||||||
| 		c.curWorkerCluster.Store(event.WorkerID, cl) | 		c.curWorkerCluster.Store(event.WorkerID, cl) | ||||||
| 
 | 
 | ||||||
|  | @ -341,7 +343,7 @@ func (c *Controller) processEvent(event ClusterEvent) { | ||||||
| 
 | 
 | ||||||
| 		// no race condition because a cluster is always processed by single worker
 | 		// no race condition because a cluster is always processed by single worker
 | ||||||
| 		if !clusterFound { | 		if !clusterFound { | ||||||
| 			cl = c.addCluster(lg, clusterName, event.NewSpec) | 			cl = c.clusterFactory.addCluster(c, lg, clusterName, event.NewSpec) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		c.curWorkerCluster.Store(event.WorkerID, cl) | 		c.curWorkerCluster.Store(event.WorkerID, cl) | ||||||
|  |  | ||||||
|  | @ -1,20 +1,101 @@ | ||||||
| package controller | package controller | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" |  | ||||||
| 	"github.com/zalando-incubator/postgres-operator/pkg/spec" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Sirupsen/logrus" | ||||||
|  | 	"k8s.io/api/core/v1" | ||||||
|  | 
 | ||||||
|  | 	acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" | ||||||
|  | 	"github.com/zalando-incubator/postgres-operator/pkg/cluster" | ||||||
|  | 	"github.com/zalando-incubator/postgres-operator/pkg/spec" | ||||||
|  | 	"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" | ||||||
|  | 	"github.com/zalando-incubator/postgres-operator/pkg/util/teams" | ||||||
|  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	v1core "k8s.io/client-go/kubernetes/typed/core/v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	True  = true | 	True   = true | ||||||
| 	False = false | 	False  = false | ||||||
|  | 	logger = logrus.New().WithField("test", "cluster") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	superUserName       = "postgres" | ||||||
|  | 	replicationUserName = "standby" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var mockTeamsAPI mockTeamsAPIClient | ||||||
|  | 
 | ||||||
|  | type mockOAuthTokenGetter struct { | ||||||
|  | 	cluster.OAuthTokenGetter | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type mockClusterGenerator struct { | ||||||
|  | 	ClusterGenerator | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type NotFoundError struct{} | ||||||
|  | 
 | ||||||
|  | func (e *NotFoundError) Error() string { | ||||||
|  | 	return "error" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (e *NotFoundError) Status() metav1.Status { | ||||||
|  | 	return metav1.Status{ | ||||||
|  | 		Reason: metav1.StatusReasonNotFound, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *mockClusterGenerator) addCluster(ctrl *Controller, | ||||||
|  | 	lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { | ||||||
|  | 
 | ||||||
|  | 	mockTeamsAPI.setMembers([]string{"test-user"}) | ||||||
|  | 	cl := cluster.New(ctrl.makeClusterConfig(), ctrl.KubeClient, *pgSpec, lg) | ||||||
|  | 	cl.SetTeamsAPIClient(&mockTeamsAPI) | ||||||
|  | 	cl.SetOAuthTokenGetter(&mockOAuthTokenGetter{}) | ||||||
|  | 	cl.Spec.TeamID = "test-team" | ||||||
|  | 	return cl | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type mockTeamsAPIClient struct { | ||||||
|  | 	members []string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *mockTeamsAPIClient) TeamInfo(teamID, token string) (tm *teams.Team, err error) { | ||||||
|  | 	return &teams.Team{Members: m.members}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *mockTeamsAPIClient) setMembers(members []string) { | ||||||
|  | 	m.members = members | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type mockNoSecretGetter struct { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type testNoSecret struct { | ||||||
|  | 	v1core.SecretInterface | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *testNoSecret) Get(name string, options metav1.GetOptions) (*v1.Secret, error) { | ||||||
|  | 	return nil, &NotFoundError{} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *mockNoSecretGetter) Secrets(namespace string) v1core.SecretInterface { | ||||||
|  | 	return &testNoSecret{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func mockKubernetesClient() k8sutil.KubernetesClient { | ||||||
|  | 	return k8sutil.KubernetesClient{ | ||||||
|  | 		SecretsGetter: &mockNoSecretGetter{}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestMergeDeprecatedPostgreSQLSpecParameters(t *testing.T) { | func TestMergeDeprecatedPostgreSQLSpecParameters(t *testing.T) { | ||||||
| 	c := NewController(&spec.ControllerConfig{}) | 	c := NewController(&spec.ControllerConfig{}) | ||||||
| 
 |  | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name  string | 		name  string | ||||||
| 		in    *acidv1.PostgresSpec | 		in    *acidv1.PostgresSpec | ||||||
|  | @ -42,3 +123,43 @@ func TestMergeDeprecatedPostgreSQLSpecParameters(t *testing.T) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestGeneratePlan(t *testing.T) { | ||||||
|  | 	c := NewController(&spec.ControllerConfig{}) | ||||||
|  | 	c.opConfig.SuperUsername = "test-superuser" | ||||||
|  | 	c.opConfig.SecretNameTemplate = "secret" | ||||||
|  | 	c.clusterFactory = &mockClusterGenerator{} | ||||||
|  | 	c.KubeClient = mockKubernetesClient() | ||||||
|  | 
 | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name     string | ||||||
|  | 		in       ClusterEvent | ||||||
|  | 		contains cluster.Plan | ||||||
|  | 		error    string | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			"Cluster add produces plan with a CreateSecret", | ||||||
|  | 			ClusterEvent{ | ||||||
|  | 				EventType: EventAdd, | ||||||
|  | 				NewSpec: &acidv1.Postgresql{ | ||||||
|  | 					ObjectMeta: metav1.ObjectMeta{ | ||||||
|  | 						Name:      "TestCluster", | ||||||
|  | 						Namespace: "TestNamespace", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			[]cluster.Action{cluster.CreateSecret{}, cluster.CreateSecret{}}, | ||||||
|  | 			"A plan for a new cluster should create secrets", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		result := c.generatePlan(tt.in) | ||||||
|  | 		for idx, output := range result { | ||||||
|  | 			output := reflect.TypeOf(output) | ||||||
|  | 			expected := reflect.TypeOf(tt.contains[idx]) | ||||||
|  | 			if output != expected { | ||||||
|  | 				t.Errorf("%s: %v", tt.name, tt.error) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -196,10 +196,10 @@ func (c *Controller) podClusterName(pod *v1.Pod) spec.NamespacedName { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func eventInSlice(a EventType, list []EventType) bool { | func eventInSlice(a EventType, list []EventType) bool { | ||||||
|     for _, b := range list { | 	for _, b := range list { | ||||||
|         if b == a { | 		if b == a { | ||||||
|             return true | 			return true | ||||||
|         } | 		} | ||||||
|     } | 	} | ||||||
|     return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue