525 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			525 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
| package controller
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 
 | |
| 	b64 "encoding/base64"
 | |
| 
 | |
| 	"github.com/zalando/postgres-operator/pkg/spec"
 | |
| 	"github.com/zalando/postgres-operator/pkg/util/config"
 | |
| 	"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	testInfrastructureRolesOldSecretName = "infrastructureroles-old-test"
 | |
| 	testInfrastructureRolesNewSecretName = "infrastructureroles-new-test"
 | |
| )
 | |
| 
 | |
| func newUtilTestController() *Controller {
 | |
| 	controller := NewController(&spec.ControllerConfig{}, "util-test")
 | |
| 	controller.opConfig.ClusterNameLabel = "cluster-name"
 | |
| 	controller.opConfig.InfrastructureRolesSecretName =
 | |
| 		spec.NamespacedName{
 | |
| 			Namespace: v1.NamespaceDefault,
 | |
| 			Name:      testInfrastructureRolesOldSecretName,
 | |
| 		}
 | |
| 	controller.opConfig.Workers = 4
 | |
| 	controller.KubeClient = k8sutil.NewMockKubernetesClient()
 | |
| 	return controller
 | |
| }
 | |
| 
 | |
| var utilTestController = newUtilTestController()
 | |
| 
 | |
| func TestPodClusterName(t *testing.T) {
 | |
| 	var testTable = []struct {
 | |
| 		in       *v1.Pod
 | |
| 		expected spec.NamespacedName
 | |
| 	}{
 | |
| 		{
 | |
| 			&v1.Pod{},
 | |
| 			spec.NamespacedName{},
 | |
| 		},
 | |
| 		{
 | |
| 			&v1.Pod{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Namespace: v1.NamespaceDefault,
 | |
| 					Labels: map[string]string{
 | |
| 						utilTestController.opConfig.ClusterNameLabel: "testcluster",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			spec.NamespacedName{Namespace: v1.NamespaceDefault, Name: "testcluster"},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range testTable {
 | |
| 		resp := utilTestController.podClusterName(test.in)
 | |
| 		if resp != test.expected {
 | |
| 			t.Errorf("expected response %v does not match the actual %v", test.expected, resp)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClusterWorkerID(t *testing.T) {
 | |
| 	var testTable = []struct {
 | |
| 		in       spec.NamespacedName
 | |
| 		expected uint32
 | |
| 	}{
 | |
| 		{
 | |
| 			in:       spec.NamespacedName{Namespace: "foo", Name: "bar"},
 | |
| 			expected: 0,
 | |
| 		},
 | |
| 		{
 | |
| 			in:       spec.NamespacedName{Namespace: "default", Name: "testcluster"},
 | |
| 			expected: 1,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range testTable {
 | |
| 		resp := utilTestController.clusterWorkerID(test.in)
 | |
| 		if resp != test.expected {
 | |
| 			t.Errorf("expected response %v does not match the actual %v", test.expected, resp)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test functionality of getting infrastructure roles from their description in
 | |
| // corresponding secrets. Here we test only common stuff (e.g. when a secret do
 | |
| // not exist, or empty) and the old format.
 | |
| func TestOldInfrastructureRoleFormat(t *testing.T) {
 | |
| 	var testTable = []struct {
 | |
| 		secretName     spec.NamespacedName
 | |
| 		expectedRoles  map[string]spec.PgUser
 | |
| 		expectedErrors []error
 | |
| 	}{
 | |
| 		{
 | |
| 			// empty secret name
 | |
| 			spec.NamespacedName{},
 | |
| 			nil,
 | |
| 			nil,
 | |
| 		},
 | |
| 		{
 | |
| 			// secret does not exist
 | |
| 			spec.NamespacedName{Namespace: v1.NamespaceDefault, Name: "null"},
 | |
| 			map[string]spec.PgUser{},
 | |
| 			[]error{fmt.Errorf(`could not get infrastructure roles secret default/null: NotFound`)},
 | |
| 		},
 | |
| 		{
 | |
| 			spec.NamespacedName{
 | |
| 				Namespace: v1.NamespaceDefault,
 | |
| 				Name:      testInfrastructureRolesOldSecretName,
 | |
| 			},
 | |
| 			map[string]spec.PgUser{
 | |
| 				"testrole": {
 | |
| 					Name:     "testrole",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: "testpassword",
 | |
| 					MemberOf: []string{"testinrole"},
 | |
| 				},
 | |
| 				"foobar": {
 | |
| 					Name:     "foobar",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: b64.StdEncoding.EncodeToString([]byte("password")),
 | |
| 					MemberOf: nil,
 | |
| 				},
 | |
| 			},
 | |
| 			nil,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range testTable {
 | |
| 		roles, errors := utilTestController.getInfrastructureRoles(
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName:  test.secretName,
 | |
| 					UserKey:     "user",
 | |
| 					PasswordKey: "password",
 | |
| 					RoleKey:     "inrole",
 | |
| 					Template:    true,
 | |
| 				},
 | |
| 			})
 | |
| 
 | |
| 		if len(errors) != len(test.expectedErrors) {
 | |
| 			t.Errorf("expected error '%v' does not match the actual error '%v'",
 | |
| 				test.expectedErrors, errors)
 | |
| 		}
 | |
| 
 | |
| 		for idx := range errors {
 | |
| 			err := errors[idx]
 | |
| 			expectedErr := test.expectedErrors[idx]
 | |
| 
 | |
| 			if err != expectedErr {
 | |
| 				if err != nil && expectedErr != nil && err.Error() == expectedErr.Error() {
 | |
| 					continue
 | |
| 				}
 | |
| 				t.Errorf("expected error '%v' does not match the actual error '%v'",
 | |
| 					expectedErr, err)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !reflect.DeepEqual(roles, test.expectedRoles) {
 | |
| 			t.Errorf("expected roles output %#v does not match the actual %#v",
 | |
| 				test.expectedRoles, roles)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test functionality of getting infrastructure roles from their description in
 | |
| // corresponding secrets. Here we test the new format.
 | |
| func TestNewInfrastructureRoleFormat(t *testing.T) {
 | |
| 	var testTable = []struct {
 | |
| 		secrets        []spec.NamespacedName
 | |
| 		expectedRoles  map[string]spec.PgUser
 | |
| 		expectedErrors []error
 | |
| 	}{
 | |
| 		// one secret with one configmap
 | |
| 		{
 | |
| 			[]spec.NamespacedName{
 | |
| 				spec.NamespacedName{
 | |
| 					Namespace: v1.NamespaceDefault,
 | |
| 					Name:      testInfrastructureRolesNewSecretName,
 | |
| 				},
 | |
| 			},
 | |
| 			map[string]spec.PgUser{
 | |
| 				"new-test-role": {
 | |
| 					Name:     "new-test-role",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: "new-test-password",
 | |
| 					MemberOf: []string{"new-test-inrole"},
 | |
| 				},
 | |
| 				"new-foobar": {
 | |
| 					Name:     "new-foobar",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: b64.StdEncoding.EncodeToString([]byte("password")),
 | |
| 					MemberOf: nil,
 | |
| 					Flags:    []string{"createdb"},
 | |
| 				},
 | |
| 			},
 | |
| 			nil,
 | |
| 		},
 | |
| 		// multiple standalone secrets
 | |
| 		{
 | |
| 			[]spec.NamespacedName{
 | |
| 				spec.NamespacedName{
 | |
| 					Namespace: v1.NamespaceDefault,
 | |
| 					Name:      "infrastructureroles-new-test1",
 | |
| 				},
 | |
| 				spec.NamespacedName{
 | |
| 					Namespace: v1.NamespaceDefault,
 | |
| 					Name:      "infrastructureroles-new-test2",
 | |
| 				},
 | |
| 			},
 | |
| 			map[string]spec.PgUser{
 | |
| 				"new-test-role1": {
 | |
| 					Name:     "new-test-role1",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: "new-test-password1",
 | |
| 					MemberOf: []string{"new-test-inrole1"},
 | |
| 				},
 | |
| 				"new-test-role2": {
 | |
| 					Name:     "new-test-role2",
 | |
| 					Origin:   spec.RoleOriginInfrastructure,
 | |
| 					Password: "new-test-password2",
 | |
| 					MemberOf: []string{"new-test-inrole2"},
 | |
| 				},
 | |
| 			},
 | |
| 			nil,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range testTable {
 | |
| 		definitions := []*config.InfrastructureRole{}
 | |
| 		for _, secret := range test.secrets {
 | |
| 			definitions = append(definitions, &config.InfrastructureRole{
 | |
| 				SecretName:  secret,
 | |
| 				UserKey:     "user",
 | |
| 				PasswordKey: "password",
 | |
| 				RoleKey:     "inrole",
 | |
| 				Template:    false,
 | |
| 			})
 | |
| 		}
 | |
| 
 | |
| 		roles, errors := utilTestController.getInfrastructureRoles(definitions)
 | |
| 		if len(errors) != len(test.expectedErrors) {
 | |
| 			t.Errorf("expected error does not match the actual error:\n%+v\n%+v",
 | |
| 				test.expectedErrors, errors)
 | |
| 
 | |
| 			// Stop and do not do any further checks
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		for idx := range errors {
 | |
| 			err := errors[idx]
 | |
| 			expectedErr := test.expectedErrors[idx]
 | |
| 
 | |
| 			if err != expectedErr {
 | |
| 				if err != nil && expectedErr != nil && err.Error() == expectedErr.Error() {
 | |
| 					continue
 | |
| 				}
 | |
| 				t.Errorf("expected error '%v' does not match the actual error '%v'",
 | |
| 					expectedErr, err)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !reflect.DeepEqual(roles, test.expectedRoles) {
 | |
| 			t.Errorf("expected roles output/the actual:\n%#v\n%#v",
 | |
| 				test.expectedRoles, roles)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Tests for getting correct infrastructure roles definitions from present
 | |
| // configuration. E.g. in which secrets for which roles too look. The biggest
 | |
| // point here is compatibility of old and new formats of defining
 | |
| // infrastructure roles.
 | |
| func TestInfrastructureRoleDefinitions(t *testing.T) {
 | |
| 	var testTable = []struct {
 | |
| 		rolesDefs      []*config.InfrastructureRole
 | |
| 		roleSecretName spec.NamespacedName
 | |
| 		roleSecrets    string
 | |
| 		expectedDefs   []*config.InfrastructureRole
 | |
| 	}{
 | |
| 		// only new CRD format
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 			},
 | |
| 			spec.NamespacedName{},
 | |
| 			"",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// only new configmap format
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{},
 | |
| 			"secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, rolekey: test-role",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// new configmap format with defaultRoleValue
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{},
 | |
| 			"secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, defaultrolevalue: test-role",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:          "test-user",
 | |
| 					PasswordKey:      "test-password",
 | |
| 					DefaultRoleValue: "test-role",
 | |
| 					Template:         false,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// only old CRD and configmap format
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{
 | |
| 				Namespace: v1.NamespaceDefault,
 | |
| 				Name:      testInfrastructureRolesOldSecretName,
 | |
| 			},
 | |
| 			"",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesOldSecretName,
 | |
| 					},
 | |
| 					UserKey:     "user",
 | |
| 					PasswordKey: "password",
 | |
| 					RoleKey:     "inrole",
 | |
| 					Template:    true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// both formats for CRD
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 			},
 | |
| 			spec.NamespacedName{
 | |
| 				Namespace: v1.NamespaceDefault,
 | |
| 				Name:      testInfrastructureRolesOldSecretName,
 | |
| 			},
 | |
| 			"",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesOldSecretName,
 | |
| 					},
 | |
| 					UserKey:     "user",
 | |
| 					PasswordKey: "password",
 | |
| 					RoleKey:     "inrole",
 | |
| 					Template:    true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// both formats for configmap
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{
 | |
| 				Namespace: v1.NamespaceDefault,
 | |
| 				Name:      testInfrastructureRolesOldSecretName,
 | |
| 			},
 | |
| 			"secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, rolekey: test-role",
 | |
| 			[]*config.InfrastructureRole{
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesNewSecretName,
 | |
| 					},
 | |
| 					UserKey:     "test-user",
 | |
| 					PasswordKey: "test-password",
 | |
| 					RoleKey:     "test-role",
 | |
| 					Template:    false,
 | |
| 				},
 | |
| 				&config.InfrastructureRole{
 | |
| 					SecretName: spec.NamespacedName{
 | |
| 						Namespace: v1.NamespaceDefault,
 | |
| 						Name:      testInfrastructureRolesOldSecretName,
 | |
| 					},
 | |
| 					UserKey:     "user",
 | |
| 					PasswordKey: "password",
 | |
| 					RoleKey:     "inrole",
 | |
| 					Template:    true,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		// incorrect configmap format
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{},
 | |
| 			"wrong-format",
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 		},
 | |
| 		// configmap without a secret
 | |
| 		{
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 			spec.NamespacedName{},
 | |
| 			"userkey: test-user, passwordkey: test-password, rolekey: test-role",
 | |
| 			[]*config.InfrastructureRole{},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range testTable {
 | |
| 		t.Logf("Test: %+v", test)
 | |
| 		utilTestController.opConfig.InfrastructureRoles = test.rolesDefs
 | |
| 		utilTestController.opConfig.InfrastructureRolesSecretName = test.roleSecretName
 | |
| 		utilTestController.opConfig.InfrastructureRolesDefs = test.roleSecrets
 | |
| 
 | |
| 		defs := utilTestController.getInfrastructureRoleDefinitions()
 | |
| 		if len(defs) != len(test.expectedDefs) {
 | |
| 			t.Errorf("expected definitions does not match the actual:\n%#v\n%#v",
 | |
| 				test.expectedDefs, defs)
 | |
| 
 | |
| 			// Stop and do not do any further checks
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		for idx := range defs {
 | |
| 			def := defs[idx]
 | |
| 			expectedDef := test.expectedDefs[idx]
 | |
| 
 | |
| 			if !reflect.DeepEqual(def, expectedDef) {
 | |
| 				t.Errorf("expected definition/the actual:\n%#v\n%#v",
 | |
| 					expectedDef, def)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type SubConfig struct {
 | |
| 	teammap map[string]string
 | |
| }
 | |
| 
 | |
| type SuperConfig struct {
 | |
| 	sub SubConfig
 | |
| }
 | |
| 
 | |
| func TestUnderstandingMapsAndReferences(t *testing.T) {
 | |
| 	teams := map[string]string{"acid": "Felix"}
 | |
| 
 | |
| 	sc := SubConfig{
 | |
| 		teammap: teams,
 | |
| 	}
 | |
| 
 | |
| 	ssc := SuperConfig{
 | |
| 		sub: sc,
 | |
| 	}
 | |
| 
 | |
| 	teams["24x7"] = "alex"
 | |
| 
 | |
| 	if len(ssc.sub.teammap) != 2 {
 | |
| 		t.Errorf("Team Map does not contain 2 elements")
 | |
| 	}
 | |
| 
 | |
| 	ssc.sub.teammap["teapot"] = "Mikkel"
 | |
| 
 | |
| 	if len(teams) != 3 {
 | |
| 		t.Errorf("Team Map does not contain 3 elements")
 | |
| 	}
 | |
| 
 | |
| 	teams = make(map[string]string)
 | |
| 
 | |
| 	if len(ssc.sub.teammap) != 3 {
 | |
| 		t.Errorf("Team Map does not contain 0 elements")
 | |
| 	}
 | |
| 
 | |
| 	if &teams == &(ssc.sub.teammap) {
 | |
| 		t.Errorf("Identical maps")
 | |
| 	}
 | |
| }
 |