Browse Source

gitlab: support "environment_scope" tag for findAll (#1732)

Signed-off-by: Dominik Zeiger <dominik@zeiger.biz>

Signed-off-by: Dominik Zeiger <dominik@zeiger.biz>
Co-authored-by: Moritz Johner <moolen@users.noreply.github.com>
Dominik Zeiger 3 years ago
parent
commit
b7100e27a0
2 changed files with 64 additions and 24 deletions
  1. 26 3
      pkg/provider/gitlab/gitlab.go
  2. 38 21
      pkg/provider/gitlab/gitlab_test.go

+ 26 - 3
pkg/provider/gitlab/gitlab.go

@@ -44,7 +44,8 @@ const (
 	errGroupAuth                              = "gitlabClient is not allowed to get secrets for group id [%s]"
 	errGroupAuth                              = "gitlabClient is not allowed to get secrets for group id [%s]"
 	errUninitializedGitlabProvider            = "provider gitlab is not initialized"
 	errUninitializedGitlabProvider            = "provider gitlab is not initialized"
 	errNameNotDefined                         = "'find.name' is mandatory"
 	errNameNotDefined                         = "'find.name' is mandatory"
-	errTagsNotImplemented                     = "'find.tags' is not currently supported by Gitlab provider"
+	errEnvironmentIsConstricted               = "'find.tags' is constrained by 'environment_scope' of the store"
+	errTagsOnlyEnvironmentSupported           = "'find.tags' only supports 'environment_scope'"
 	errPathNotImplemented                     = "'find.path' is not implemented in the Gitlab provider"
 	errPathNotImplemented                     = "'find.path' is not implemented in the Gitlab provider"
 	errJSONSecretUnmarshal                    = "unable to unmarshal secret: %w"
 	errJSONSecretUnmarshal                    = "unable to unmarshal secret: %w"
 )
 )
@@ -192,7 +193,14 @@ func (g *Gitlab) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecret
 		return nil, fmt.Errorf(errUninitializedGitlabProvider)
 		return nil, fmt.Errorf(errUninitializedGitlabProvider)
 	}
 	}
 	if ref.Tags != nil {
 	if ref.Tags != nil {
-		return nil, fmt.Errorf(errTagsNotImplemented)
+		environment, err := ExtractTag(ref.Tags)
+		if err != nil {
+			return nil, err
+		}
+		if !isEmptyOrWildcard(g.environment) && !isEmptyOrWildcard(environment) {
+			return nil, fmt.Errorf(errEnvironmentIsConstricted)
+		}
+		g.environment = environment
 	}
 	}
 	if ref.Path != nil {
 	if ref.Path != nil {
 		return nil, fmt.Errorf(errPathNotImplemented)
 		return nil, fmt.Errorf(errPathNotImplemented)
@@ -248,6 +256,17 @@ func (g *Gitlab) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecret
 	return secretData, nil
 	return secretData, nil
 }
 }
 
 
+func ExtractTag(tags map[string]string) (string, error) {
+	var environmentScope string
+	for tag, value := range tags {
+		if tag != "environment_scope" {
+			return "", fmt.Errorf(errTagsOnlyEnvironmentSupported)
+		}
+		environmentScope = value
+	}
+	return environmentScope, nil
+}
+
 func (g *Gitlab) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 func (g *Gitlab) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	if utils.IsNil(g.projectVariablesClient) || utils.IsNil(g.groupVariablesClient) {
 	if utils.IsNil(g.projectVariablesClient) || utils.IsNil(g.groupVariablesClient) {
 		return nil, fmt.Errorf(errUninitializedGitlabProvider)
 		return nil, fmt.Errorf(errUninitializedGitlabProvider)
@@ -347,8 +366,12 @@ func (g *Gitlab) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretD
 	return secretData, nil
 	return secretData, nil
 }
 }
 
 
+func isEmptyOrWildcard(environment string) bool {
+	return environment == "" || environment == "*"
+}
+
 func matchesFilter(environment, varEnvironment, key string, matcher *find.Matcher) (bool, string) {
 func matchesFilter(environment, varEnvironment, key string, matcher *find.Matcher) (bool, string) {
-	if environment != "" && environment != "*" {
+	if !isEmptyOrWildcard(environment) {
 		// as of now gitlab does not support filtering of EnvironmentScope through the api call
 		// as of now gitlab does not support filtering of EnvironmentScope through the api call
 		if varEnvironment != environment {
 		if varEnvironment != environment {
 			return false, ""
 			return false, ""

+ 38 - 21
pkg/provider/gitlab/gitlab_test.go

@@ -44,7 +44,7 @@ const (
 	projectvalue          = "projectvalue"
 	projectvalue          = "projectvalue"
 	groupvalue            = "groupvalue"
 	groupvalue            = "groupvalue"
 	groupid               = "groupId"
 	groupid               = "groupId"
-	defaultErrorMessage   = "[%d] unexpected error: %s, expected: '%s'"
+	defaultErrorMessage   = "[%d] unexpected error: [%s], expected: [%s]"
 	errMissingCredentials = "credentials are empty"
 	errMissingCredentials = "credentials are empty"
 	findTestPrefix        = "test.*"
 	findTestPrefix        = "test.*"
 )
 )
@@ -404,7 +404,7 @@ func TestGetSecret(t *testing.T) {
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 		}
 		}
 		if string(out) != v.expectedSecret {
 		if string(out) != v.expectedSecret {
-			t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
+			t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out), v.expectedSecret)
 		}
 		}
 	}
 	}
 }
 }
@@ -420,7 +420,7 @@ func TestResolveGroupIds(t *testing.T) {
 		t.Errorf(defaultErrorMessage, 0, err.Error(), "")
 		t.Errorf(defaultErrorMessage, 0, err.Error(), "")
 	}
 	}
 	if !reflect.DeepEqual(sm.groupIDs, []string{"1", "10", "100"}) {
 	if !reflect.DeepEqual(sm.groupIDs, []string{"1", "10", "100"}) {
-		t.Errorf("Expected groupIds %s, got %s", []string{"1", "10", "100"}, sm.groupIDs)
+		t.Errorf("unexpected groupIds: %s, expected %s", sm.groupIDs, []string{"1", "10", "100"})
 	}
 	}
 }
 }
 
 
@@ -432,28 +432,28 @@ func TestGetAllSecrets(t *testing.T) {
 		smtc.refFind.Name = nil
 		smtc.refFind.Name = nil
 		smtc.expectError = "'find.name' is mandatory"
 		smtc.expectError = "'find.name' is mandatory"
 	}
 	}
-	setUnsupportedFindTags := func(smtc *secretManagerTestCase) {
-		smtc.refFind.Tags = map[string]string{}
-		smtc.expectError = "'find.tags' is not currently supported by Gitlab provider"
-	}
 	setUnsupportedFindPath := func(smtc *secretManagerTestCase) {
 	setUnsupportedFindPath := func(smtc *secretManagerTestCase) {
 		path := "path"
 		path := "path"
 		smtc.refFind.Path = &path
 		smtc.refFind.Path = &path
 		smtc.expectError = "'find.path' is not implemented in the Gitlab provider"
 		smtc.expectError = "'find.path' is not implemented in the Gitlab provider"
 	}
 	}
+	setUnsupportedFindTag := func(smtc *secretManagerTestCase) {
+		smtc.expectError = "'find.tags' only supports 'environment_scope"
+		smtc.refFind.Tags = map[string]string{"foo": ""}
+	}
 	setMatchingSecretFindString := func(smtc *secretManagerTestCase) {
 	setMatchingSecretFindString := func(smtc *secretManagerTestCase) {
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 			Key:              "testkey",
 			Key:              "testkey",
-			Value:            "changedvalue",
-			EnvironmentScope: "test",
+			Value:            projectvalue,
+			EnvironmentScope: environment,
 		}
 		}
-		smtc.expectedSecret = "changedvalue"
+		smtc.expectedSecret = projectvalue
 		smtc.refFind.Name = makeFindName(findTestPrefix)
 		smtc.refFind.Name = makeFindName(findTestPrefix)
 	}
 	}
 	setNoMatchingRegexpFindString := func(smtc *secretManagerTestCase) {
 	setNoMatchingRegexpFindString := func(smtc *secretManagerTestCase) {
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 			Key:              "testkey",
 			Key:              "testkey",
-			Value:            "changedvalue",
+			Value:            projectvalue,
 			EnvironmentScope: "test",
 			EnvironmentScope: "test",
 		}
 		}
 		smtc.expectedSecret = ""
 		smtc.expectedSecret = ""
@@ -462,27 +462,44 @@ func TestGetAllSecrets(t *testing.T) {
 	setUnmatchedEnvironmentFindString := func(smtc *secretManagerTestCase) {
 	setUnmatchedEnvironmentFindString := func(smtc *secretManagerTestCase) {
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 		smtc.projectAPIOutput = &gitlab.ProjectVariable{
 			Key:              "testkey",
 			Key:              "testkey",
-			Value:            "changedvalue",
-			EnvironmentScope: "prod",
+			Value:            projectvalue,
+			EnvironmentScope: "test",
 		}
 		}
 		smtc.expectedSecret = ""
 		smtc.expectedSecret = ""
 		smtc.refFind.Name = makeFindName(findTestPrefix)
 		smtc.refFind.Name = makeFindName(findTestPrefix)
 	}
 	}
+	setMatchingSecretFindTags := func(smtc *secretManagerTestCase) {
+		smtc.projectAPIOutput = &gitlab.ProjectVariable{
+			Key:              "testkey",
+			Value:            projectvalue,
+			EnvironmentScope: environment,
+		}
+		smtc.apiInputEnv = "*"
+		smtc.expectedSecret = projectvalue
+		smtc.refFind.Tags = map[string]string{"environment_scope": environment}
+	}
+	setEnvironmentConstrainedByStore := func(smtc *secretManagerTestCase) {
+		smtc.expectedSecret = projectvalue
+		smtc.expectError = "'find.tags' is constrained by 'environment_scope' of the store"
+		smtc.refFind.Tags = map[string]string{"environment_scope": environment}
+	}
 
 
 	cases := []*secretManagerTestCase{
 	cases := []*secretManagerTestCase{
 		makeValidSecretManagerGetAllTestCaseCustom(setMissingFindRegex),
 		makeValidSecretManagerGetAllTestCaseCustom(setMissingFindRegex),
-		makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindTags),
 		makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindPath),
 		makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindPath),
+		makeValidSecretManagerGetAllTestCaseCustom(setUnsupportedFindTag),
 		makeValidSecretManagerGetAllTestCaseCustom(setMatchingSecretFindString),
 		makeValidSecretManagerGetAllTestCaseCustom(setMatchingSecretFindString),
 		makeValidSecretManagerGetAllTestCaseCustom(setNoMatchingRegexpFindString),
 		makeValidSecretManagerGetAllTestCaseCustom(setNoMatchingRegexpFindString),
 		makeValidSecretManagerGetAllTestCaseCustom(setUnmatchedEnvironmentFindString),
 		makeValidSecretManagerGetAllTestCaseCustom(setUnmatchedEnvironmentFindString),
+		makeValidSecretManagerGetAllTestCaseCustom(setMatchingSecretFindTags),
+		makeValidSecretManagerGetAllTestCaseCustom(setEnvironmentConstrainedByStore),
 		makeValidSecretManagerGetAllTestCaseCustom(setAPIErr),
 		makeValidSecretManagerGetAllTestCaseCustom(setAPIErr),
 		makeValidSecretManagerGetAllTestCaseCustom(setNilMockClient),
 		makeValidSecretManagerGetAllTestCaseCustom(setNilMockClient),
 	}
 	}
 
 
 	sm := Gitlab{}
 	sm := Gitlab{}
-	sm.environment = "test"
 	for k, v := range cases {
 	for k, v := range cases {
+		sm.environment = v.apiInputEnv
 		sm.projectVariablesClient = v.mockProjectVarClient
 		sm.projectVariablesClient = v.mockProjectVarClient
 		sm.groupVariablesClient = v.mockGroupVarClient
 		sm.groupVariablesClient = v.mockGroupVarClient
 		out, err := sm.GetAllSecrets(context.Background(), *v.refFind)
 		out, err := sm.GetAllSecrets(context.Background(), *v.refFind)
@@ -490,7 +507,7 @@ func TestGetAllSecrets(t *testing.T) {
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 		}
 		}
 		if v.expectError == "" && string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
 		if v.expectError == "" && string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
-			t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out[v.projectAPIOutput.Key]))
+			t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out[v.projectAPIOutput.Key]), v.expectedSecret)
 		}
 		}
 	}
 	}
 }
 }
@@ -549,10 +566,10 @@ func TestGetAllSecretsWithGroups(t *testing.T) {
 		if v.expectError == "" {
 		if v.expectError == "" {
 			if len(v.expectedData) > 0 {
 			if len(v.expectedData) > 0 {
 				if !reflect.DeepEqual(v.expectedData, out) {
 				if !reflect.DeepEqual(v.expectedData, out) {
-					t.Errorf("[%d] Unexpected secrets. Expected [%s], got [%s]", k, v.expectedData, out)
+					t.Errorf("[%d] unexpected secrets: [%s], expected [%s]", k, out, v.expectedData)
 				}
 				}
 			} else if string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
 			} else if string(out[v.projectAPIOutput.Key]) != v.expectedSecret {
-				t.Errorf("[%d] Unexpected secret. Expected [%s], got [%s]", k, v.expectedSecret, string(out[v.projectAPIOutput.Key]))
+				t.Errorf("[%d] unexpected secret: [%s], expected [%s]", k, string(out[v.projectAPIOutput.Key]), v.expectedSecret)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -583,10 +600,10 @@ func TestValidate(t *testing.T) {
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 		}
 		}
 		if validationResult != v.expectedValidationResult {
 		if validationResult != v.expectedValidationResult {
-			t.Errorf("[%d], unexpected validationResult: %s, expected: '%s'", k, validationResult, v.expectedValidationResult)
+			t.Errorf("[%d], unexpected validationResult: [%s], expected: [%s]", k, validationResult, v.expectedValidationResult)
 		}
 		}
 		if sm.inheritFromGroups && sm.groupIDs[0] != "1" {
 		if sm.inheritFromGroups && sm.groupIDs[0] != "1" {
-			t.Errorf("[%d], Expected groupID '1'", k)
+			t.Errorf("[%d], unexpected groupID: [%s], expected [1]", k, sm.groupIDs[0])
 		}
 		}
 	}
 	}
 }
 }
@@ -620,7 +637,7 @@ func TestGetSecretMap(t *testing.T) {
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 			t.Errorf(defaultErrorMessage, k, err.Error(), v.expectError)
 		}
 		}
 		if err == nil && !reflect.DeepEqual(out, v.expectedData) {
 		if err == nil && !reflect.DeepEqual(out, v.expectedData) {
-			t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
+			t.Errorf("[%d] unexpected secret data: [%#v], expected [%#v]", k, out, v.expectedData)
 		}
 		}
 	}
 	}
 }
 }