diff --git a/pkg/controller/validate/validate.go b/pkg/controller/validate/validate.go index cc6eed68e7..75907ddc9c 100644 --- a/pkg/controller/validate/validate.go +++ b/pkg/controller/validate/validate.go @@ -1,9 +1,14 @@ package validate import ( + "crypto/x509" + "encoding/json" + "encoding/pem" "errors" "fmt" + "net/url" "reflect" + "strings" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -13,6 +18,20 @@ import ( mdbv1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1" ) +type googleServiceAccountKey struct { + Type string `json:"type"` + ProjectID string `json:"project_id"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` // Expects valid PEM key + ClientEmail string `json:"client_email"` // Expects a valid email + ClientID string `json:"client_id"` + AuthURI string `json:"auth_uri"` // Expects valid URL + TokenURI string `json:"token_uri"` // Expects valid URL + AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` // Expects valid URL + ClientX509CertURL string `json:"client_x509_cert_url"` // Expects valid URL + UniverseDomain string `json:"universe_domain"` +} + func DeploymentSpec(deploymentSpec mdbv1.AtlasDeploymentSpec) error { var err error @@ -53,6 +72,10 @@ func Project(project *mdbv1.AtlasProject) error { return err } + if err := encryptionAtRest(project.Spec.EncryptionAtRest); err != nil { + return err + } + return nil } @@ -196,3 +219,58 @@ func projectCustomRoles(customRoles []mdbv1.CustomRole) error { return err } + +func encryptionAtRest(encryption *mdbv1.EncryptionAtRest) error { + if encryption != nil && + encryption.GoogleCloudKms.Enabled != nil && + *encryption.GoogleCloudKms.Enabled { + if encryption.GoogleCloudKms.ServiceAccountKey == "" { + return fmt.Errorf("missing Google Service Account Key but GCP KMS is enabled") + } + if err := gceServiceAccountKey(encryption.GoogleCloudKms.ServiceAccountKey); err != nil { + return fmt.Errorf("failed to validate Google Service Account Key: %w", err) + } + } + return nil +} + +func unfilter(key string) string { + return strings.ReplaceAll(key, "\\\\n", "\\n") +} + +func gceServiceAccountKey(key string) error { + emptyKey := googleServiceAccountKey{} + gceSAKey := googleServiceAccountKey{} + if err := json.Unmarshal(([]byte)(unfilter(key)), &gceSAKey); err != nil { + return fmt.Errorf("invalid service account key format: %w", err) + } + if emptyKey == gceSAKey { + return fmt.Errorf("invalid empty service account key") + } + for _, rawURL := range []string{gceSAKey.AuthURI, + gceSAKey.TokenURI, + gceSAKey.ClientX509CertURL, + gceSAKey.AuthProviderX509CertURL} { + if _, err := url.ParseRequestURI(rawURL); err != nil { + return fmt.Errorf("invalid URL address %q: %w", rawURL, err) + } + } + block, _ := pem.Decode([]byte(gceSAKey.PrivateKey)) + if block == nil || !strings.HasSuffix(block.Type, "PRIVATE KEY") { + return fmt.Errorf("failed to decode PEM block containing a private key") + } + + err := assertParsePrivateKey(block.Bytes) + if err != nil { + return fmt.Errorf("failed to parse PEM private key: %w", err) + } + return nil +} + +func assertParsePrivateKey(key []byte) error { + _, err := x509.ParsePKCS1PrivateKey(key) + if err != nil && strings.Contains(err.Error(), "ParsePKCS8PrivateKey") { + _, err = x509.ParsePKCS8PrivateKey(key) + } + return err +} diff --git a/pkg/controller/validate/validate_test.go b/pkg/controller/validate/validate_test.go index a81a1a421c..71d8e4fddf 100644 --- a/pkg/controller/validate/validate_test.go +++ b/pkg/controller/validate/validate_test.go @@ -1,6 +1,7 @@ package validate import ( + "fmt" "testing" "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/status" @@ -441,3 +442,96 @@ func TestBackupScheduleValidation(t *testing.T) { }) }) } + +// this key was removed immediately after download, so don't bother +const sampleSAKey = `{ + "type": "service_account", + "project_id": "some-project", + "private_key_id": "dc6c401f0acd0147ca70e3169f579b570583b58f", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCviL4+Bnn759sV\nNrtyexwHtR/5JYzwivupqOMdz1zuscqKfJOo6RXzq7Em3saoxLpHwwJzPq+HX1D+\ndaE0fB2hO0Mfjkcgmro0jBbLnRJgFCx7NwkyDfj8z6i4zx2CxZLiwdqo3Q3hEMNy\n5oZs52tFlTkOALCxa76Aq4eUIblupyhlhETQB1fb4D9+U57b4eeeFRyccwR7Cg0x\nfZUE1udV7YwKphLS8dCbLoqAQ3jmaxv+Qjo8e5Mj5oaMxzRAdOO1VtG9GaLU96Rc\nKGS75w+E/eR3Pm7b2dFo3jba2jvgm3U++EJi5/0zL/TQnOMwNHASPUsYQAhZezB5\nh5/MDwmjAgMBAAECggEAIOccZer33Zipz9GpFD3wVJ+GZUC9KO+cWcJ/A/z1Ggb4\nhLnyQbSjOUAjHjqe+U6a7k2m/WwwIctjlrb85yYmtayymc0lFv75zVS/Bx6jrZ/K\ncLQxxJCq7dSM90tXaEZZkKiusH1zFw9522VLqEk+qdXdUnsdo7wjAuJkMQebRxq8\n1lp3UGqAraBXLRrYUnQwBRezSYh93nZ+u+etjRCBjMYoy06PGDrJG05/FFNgQy1y\nGkBVKnmf9WNyDuX1ePyoXCAvRkwUW5ixTNGoGrRwbwleaFTvYPBlPM+TyCw4rdnU\nzRVNpoCqkf6S9tXjHKmGgwkyMqmYMCOk/5xCSb2p4QKBgQD14xcXIf9lnyHJCllU\nQdUIAmIp9/91Rdjpf/y98LCNrD/cyTvVr0+xSnN9ksBEGwvTIaaBcvdCMNBG8sI9\nsnO8W8GG1Bs80D3XIbJFaGmTmOngvrfie3tbP77wfcPgn659Q0I1+bNZGNVX+WEM\nn+f7rfGPa8Br/zHpQf2gaGWHAwKBgQC2wOrInJ+ndpqU49JVaT6YtQj0OKeLhSpc\n0N5DdW0jjD0WrnhOsLUMPC5V8R5fo4tFPfXVIfE2k8J5xxgopJzGLlHmhxq0ltmF\nbSoM2uHKf0UiRFmVTwZmzDwn+Ym/H9J/6L7Gd86u8kmWfJYFa3OzqJTvw7e+k4kD\nITb/NlEg4QKBgQCfW4AZg/Ur/Ug+LTDbxJa2TCUmog20CYKdQk+hIh6qktoI03qt\n8KKrel8DIVruSMEPIp3xA3twMIarlKWCqucLSkRQh6LndOa/SJ1rElJqUA4zlCdE\n51Z5OwUag8exCoxhrnd4183+jnOmQn89WV1V5dPKacEZvRix3gzsKvyx1QKBgFsH\nlOsAOPYtOapYIHiyx59A7YjYf3wbhJJe55cqcoZ2YCdgGET59/R0NZBRXhO9Xq3K\nwxy6n2/UAdauuPXlqMF+aQUu3rp9OTQgwAVPMZCv/DupWAXrKwEhUgWHYnl03GEi\nCYTKQIUb4lO3EvL4JtWiby1Oi8O9sU2ByectoxOBAoGASm9BXSN8Ru+dP5/E55mb\nd//aQlxlIvROhWSnotGzhyQ6DVk2fRRZQAuTEFVEprBX87gckdzb5cdaomziO9be\nhmtv1ValgmOCnta2AYw1blvfGK7B5FEpFckMniLjWap08aironIImj6ligLWDqc0\nNbdyAvc6N/5qG8gu4f8C2Q4=\n-----END PRIVATE KEY-----\n", + "client_email": "619108922856-compute@developer.gserviceaccount.com", + "client_id": "117865750705662546099", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/619108922856-compute%40developer.gserviceaccount.com", + "universe_domain": "googleapis.com" +}` + +const sampleSAKeyOneLine = `{ "type": "service_account", "project_id": "some-project", "private_key_id": "dc6c401f0acd0147ca70e3169f579b570583b58f", "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCviL4+Bnn759sV\\nNrtyexwHtR/5JYzwivupqOMdz1zuscqKfJOo6RXzq7Em3saoxLpHwwJzPq+HX1D+\\ndaE0fB2hO0Mfjkcgmro0jBbLnRJgFCx7NwkyDfj8z6i4zx2CxZLiwdqo3Q3hEMNy\\n5oZs52tFlTkOALCxa76Aq4eUIblupyhlhETQB1fb4D9+U57b4eeeFRyccwR7Cg0x\\nfZUE1udV7YwKphLS8dCbLoqAQ3jmaxv+Qjo8e5Mj5oaMxzRAdOO1VtG9GaLU96Rc\\nKGS75w+E/eR3Pm7b2dFo3jba2jvgm3U++EJi5/0zL/TQnOMwNHASPUsYQAhZezB5\\nh5/MDwmjAgMBAAECggEAIOccZer33Zipz9GpFD3wVJ+GZUC9KO+cWcJ/A/z1Ggb4\\nhLnyQbSjOUAjHjqe+U6a7k2m/WwwIctjlrb85yYmtayymc0lFv75zVS/Bx6jrZ/K\\ncLQxxJCq7dSM90tXaEZZkKiusH1zFw9522VLqEk+qdXdUnsdo7wjAuJkMQebRxq8\\n1lp3UGqAraBXLRrYUnQwBRezSYh93nZ+u+etjRCBjMYoy06PGDrJG05/FFNgQy1y\\nGkBVKnmf9WNyDuX1ePyoXCAvRkwUW5ixTNGoGrRwbwleaFTvYPBlPM+TyCw4rdnU\\nzRVNpoCqkf6S9tXjHKmGgwkyMqmYMCOk/5xCSb2p4QKBgQD14xcXIf9lnyHJCllU\\nQdUIAmIp9/91Rdjpf/y98LCNrD/cyTvVr0+xSnN9ksBEGwvTIaaBcvdCMNBG8sI9\\nsnO8W8GG1Bs80D3XIbJFaGmTmOngvrfie3tbP77wfcPgn659Q0I1+bNZGNVX+WEM\\nn+f7rfGPa8Br/zHpQf2gaGWHAwKBgQC2wOrInJ+ndpqU49JVaT6YtQj0OKeLhSpc\\n0N5DdW0jjD0WrnhOsLUMPC5V8R5fo4tFPfXVIfE2k8J5xxgopJzGLlHmhxq0ltmF\\nbSoM2uHKf0UiRFmVTwZmzDwn+Ym/H9J/6L7Gd86u8kmWfJYFa3OzqJTvw7e+k4kD\\nITb/NlEg4QKBgQCfW4AZg/Ur/Ug+LTDbxJa2TCUmog20CYKdQk+hIh6qktoI03qt\\n8KKrel8DIVruSMEPIp3xA3twMIarlKWCqucLSkRQh6LndOa/SJ1rElJqUA4zlCdE\\n51Z5OwUag8exCoxhrnd4183+jnOmQn89WV1V5dPKacEZvRix3gzsKvyx1QKBgFsH\\nlOsAOPYtOapYIHiyx59A7YjYf3wbhJJe55cqcoZ2YCdgGET59/R0NZBRXhO9Xq3K\\nwxy6n2/UAdauuPXlqMF+aQUu3rp9OTQgwAVPMZCv/DupWAXrKwEhUgWHYnl03GEi\\nCYTKQIUb4lO3EvL4JtWiby1Oi8O9sU2ByectoxOBAoGASm9BXSN8Ru+dP5/E55mb\\nd//aQlxlIvROhWSnotGzhyQ6DVk2fRRZQAuTEFVEprBX87gckdzb5cdaomziO9be\\nhmtv1ValgmOCnta2AYw1blvfGK7B5FEpFckMniLjWap08aironIImj6ligLWDqc0\\nNbdyAvc6N/5qG8gu4f8C2Q4=\\n-----END PRIVATE KEY-----\\n", "client_email": "619108922856-compute@developer.gserviceaccount.com", "client_id": "117865750705662546099", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/619108922856-compute%40developer.gserviceaccount.com", "universe_domain": "googleapis.com"}` + +func testEncryptionAtRest(enabled bool) *mdbv1.EncryptionAtRest { + flag := enabled + return &mdbv1.EncryptionAtRest{ + GoogleCloudKms: mdbv1.GoogleCloudKms{ + Enabled: &flag, + ServiceAccountKey: sampleSAKey, + }, + } +} + +func withProperUrls(properties string) string { + urls := `"auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/619108922856-compute%40developer.gserviceaccount.com"` + return fmt.Sprintf(`{%s, %s}`, urls, properties) +} + +func TestEncryptionAtRestValidation(t *testing.T) { + t.Run("google service account key validation succeeds if no encryption at rest is used", func(t *testing.T) { + assert.NoError(t, encryptionAtRest(&mdbv1.EncryptionAtRest{})) + }) + + t.Run("google service account key validation succeeds if encryption at rest is disabled", func(t *testing.T) { + assert.NoError(t, encryptionAtRest(testEncryptionAtRest(false))) + }) + + t.Run("google service account key validation succeeds if encryption is enabled but the key is empty", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = "" + assert.ErrorContains(t, encryptionAtRest(enc), "missing Google Service Account Key but GCP KMS is enabled") + }) + + t.Run("google service account key validation succeeds for a good key", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = sampleSAKey + assert.NoError(t, encryptionAtRest(enc)) + }) + + t.Run("google service account key validation succeeds for a good key in a single line", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = sampleSAKeyOneLine + assert.NoError(t, encryptionAtRest(enc)) + }) + + t.Run("google service account key validation fails for an empty json key", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = "{}" + assert.ErrorContains(t, encryptionAtRest(enc), "invalid empty service account key") + }) + + t.Run("google service account key validation fails for an empty array json as key", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = "[]" + assert.ErrorContains(t, encryptionAtRest(enc), "cannot unmarshal array into Go value") + }) + + t.Run("google service account key validation fails for a json object with a wrong field type", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = `{"type":true}` + assert.ErrorContains(t, encryptionAtRest(enc), "cannot unmarshal bool") + }) + + t.Run("google service account key validation fails for a bad pem key", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = withProperUrls(`"private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvQblah\n-----END PRIVATE KEY-----\n"`) + assert.ErrorContains(t, encryptionAtRest(enc), "failed to decode PEM") + }) + + t.Run("google service account key validation fails for a bad URL", func(t *testing.T) { + enc := testEncryptionAtRest(true) + enc.GoogleCloudKms.ServiceAccountKey = withProperUrls(`"token_uri": "http//badurl.example"`) + assert.ErrorContains(t, encryptionAtRest(enc), "invalid URL address") + }) +} diff --git a/test/e2e/encryption_at_rest_test.go b/test/e2e/encryption_at_rest_test.go index 8682bf366d..6f0fba88b4 100644 --- a/test/e2e/encryption_at_rest_test.go +++ b/test/e2e/encryption_at_rest_test.go @@ -1,16 +1,25 @@ package e2e_test import ( + "bufio" + "bytes" + "context" + "fmt" + "os" + "strings" "time" "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.mongodb.org/atlas/mongodbatlas" + "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/types" v1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1" "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/status" + "github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/workflow" + "github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/testutil" "github.com/mongodb/mongodb-atlas-kubernetes/pkg/util/toptr" "github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/actions" "github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/actions/cloud" @@ -294,3 +303,191 @@ var _ = Describe("Encryption at rest AWS", Label("encryption-at-rest"), func() { }) }) }) + +func configureManager(testData *model.TestDataProvider) { + mgr := actions.PrepareOperatorConfigurations(testData) + ctx := context.Background() + go func(ctx context.Context) context.Context { + err := mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + return ctx + }(ctx) + testData.ManagerContext = ctx +} + +func createProjectWithValidationError(testData *model.TestDataProvider, errMsg string) { + if testData.Project.GetNamespace() == "" { + testData.Project.Namespace = testData.Resources.Namespace + } + By(fmt.Sprintf("Deploy Broken Project %s", testData.Project.GetName()), func() { + err := testData.K8SClient.Create(testData.Context, testData.Project) + Expect(err).ShouldNot(HaveOccurred(), "Project %s was not created", testData.Project.GetName()) + expectedCondition := + status.FalseCondition(status.ValidationSucceeded).WithReason(string(workflow.Internal)).WithMessageRegexp(errMsg) + Eventually(func() bool { + return testutil.CheckCondition(testData.K8SClient, testData.Project, expectedCondition) + }).WithPolling(3 * time.Second).WithTimeout(40 * time.Second).Should(BeTrue()) + }) +} + +func withProperUrls(properties string) string { + urls := `"auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/619108922856-compute%40developer.gserviceaccount.com"` + return fmt.Sprintf(`{%s, %s}`, urls, properties) +} + +func repeat(unit string, times int) string { + var buf strings.Builder + for i := 0; i < times; i++ { + buf.WriteString(unit) + } + return buf.String() +} + +func yamlMultiline(indentation int, s string) string { + indentPrefix := repeat(" ", indentation) + var buf strings.Builder + fmt.Fprintf(&buf, "|\n") + scanner := bufio.NewScanner(bytes.NewBufferString(s)) + for scanner.Scan() { + line := scanner.Text() + fmt.Fprintf(&buf, "%s%s\n", indentPrefix, line) + } + if err := scanner.Err(); err != nil { + return err.Error() + } + return buf.String() +} + +const projectWithGceEncryptionFmt = `apiVersion: atlas.mongodb.com/v1 +kind: AtlasProject +metadata: + name: my-project +spec: + name: Test Atlas Operator Project + encryptionAtRest: + googleCloudKms: + enabled: true + keyVersionResourceID: %s + serviceAccountKey: %s` + +// composeGoogleEncryptionAtRestProjectYAML produces something like this YAML: +// +// apiVersion: atlas.mongodb.com/v1 +// kind: AtlasProject +// metadata: +// name: my-project +// spec: +// name: Test Atlas Operator Project +// encryptionAtRest: +// googleCloudKms: +// enabled: true +// keyVersionResourceID: projects/... +// serviceAccountKey: | +// { +// "type": "service_account", +// "project_id": "...", +// ... +// } +func composeGoogleEncryptionAtRestProjectYAML(serviceAccountKey, keyVersionResourceID string) string { + return fmt.Sprintf(projectWithGceEncryptionFmt, keyVersionResourceID, yamlMultiline(8, serviceAccountKey)) +} + +var _ = Describe("Encryption at rest GCP key validation", Label("encryption-at-rest"), func() { + var testData *model.TestDataProvider + _ = BeforeEach(func() { + checkUpEnvironment() + checkNSetUpGCPEnvironment() + testData = model.DataProvider( + "ear-gcp-key-validation", + model.NewEmptyAtlasKeyType().UseDefaultFullAccess(), + 40000, + []func(*model.TestDataProvider){}, + ).WithProject(data.DefaultProject()) + }) + + _ = AfterEach(func() { + GinkgoWriter.Write([]byte("\n")) + GinkgoWriter.Write([]byte("===============================================\n")) + GinkgoWriter.Write([]byte("Operator namespace: " + testData.Resources.Namespace + "\n")) + GinkgoWriter.Write([]byte("===============================================\n")) + if CurrentSpecReport().Failed() { + Expect(actions.SaveProjectsToFile(testData.Context, testData.K8SClient, testData.Resources.Namespace)).Should(Succeed()) + } + + By("Delete Resources, Project with Encryption at rest", func() { + actions.DeleteTestDataProject(testData) + actions.AfterEachFinalCleanup([]model.TestDataProvider{*testData}) + }) + }) + + DescribeTable("fails if the service account key", + func(encryption *v1.EncryptionAtRest, errMsg string) { + testData.Project.Spec.EncryptionAtRest = encryption + configureManager(testData) + createProjectWithValidationError(testData, errMsg) + }, + Entry( + "is missing", + &v1.EncryptionAtRest{ + GoogleCloudKms: v1.GoogleCloudKms{ + Enabled: toptr.MakePtr(true), + ServiceAccountKey: "", + }, + }, + "missing Google Service Account Key but GCP KMS is enabled", + ), + Entry( + "is an empty JSON object", + &v1.EncryptionAtRest{ + GoogleCloudKms: v1.GoogleCloudKms{ + Enabled: toptr.MakePtr(true), + ServiceAccountKey: "{}", + }, + }, + "invalid empty service account key", + ), + Entry( + "is an empty JSON array", + &v1.EncryptionAtRest{ + GoogleCloudKms: v1.GoogleCloudKms{ + Enabled: toptr.MakePtr(true), + ServiceAccountKey: "[]", + }, + }, + "cannot unmarshal array into Go value", + ), + Entry( + "has a bad PEM string", + &v1.EncryptionAtRest{ + GoogleCloudKms: v1.GoogleCloudKms{ + Enabled: toptr.MakePtr(true), + ServiceAccountKey: withProperUrls(`"private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvQblah\n-----END PRIVATE KEY-----\n"`), + }, + }, + "failed to decode PEM block", + ), + Entry( + "contains a bad URL", + &v1.EncryptionAtRest{ + GoogleCloudKms: v1.GoogleCloudKms{ + Enabled: toptr.MakePtr(true), + ServiceAccountKey: withProperUrls(`"token_uri": "http//badurl.example"`), + }, + }, + "invalid URL address", + ), + ) + + It("correct project works", func() { + projectYAML := composeGoogleEncryptionAtRestProjectYAML( + os.Getenv("GCP_SA_CRED"), + os.Getenv("GOOGLE_KEY_VERSION_RESOURCE_ID"), + ) + Expect(yaml.Unmarshal(([]byte)(projectYAML), testData.Project)).ToNot(HaveOccurred()) + + actions.ProjectCreationFlow(testData) + }) +})