blob: 49dec87b2526f996fda92830013d2ed0b79b5321 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package deploymentmanager_test
import (
"bytes"
"fmt"
"io/ioutil"
"regexp"
"strings"
"testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
)
func TestAccDeploymentManagerDeployment_basicFile(t *testing.T) {
t.Parallel()
randSuffix := acctest.RandString(t, 10)
deploymentId := "tf-dm-" + randSuffix
accountId := "tf-dm-account-" + randSuffix
yamlPath := createYamlConfigFileForTest(t, "test-fixtures/service_account.yml.tmpl", map[string]interface{}{
"account_id": accountId,
})
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckDeploymentManagerDeploymentDestroyProducer(t),
testDeploymentManagerDeploymentVerifyServiceAccountMissing(t, accountId)),
Steps: []resource.TestStep{
{
Config: testAccDeploymentManagerDeployment_basicFile(deploymentId, yamlPath),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
},
})
}
func TestAccDeploymentManagerDeployment_deleteInvalidOnCreate(t *testing.T) {
t.Parallel()
randStr := acctest.RandString(t, 10)
deploymentName := "tf-dm-" + randStr
accountId := "tf-dm-" + randStr
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckDeploymentManagerDestroyInvalidDeployment(t, deploymentName),
Steps: []resource.TestStep{
{
Config: testAccDeploymentManagerDeployment_invalidCreatePolicy(deploymentName, accountId),
ExpectError: regexp.MustCompile("BAD REQUEST"),
},
},
})
}
func TestAccDeploymentManagerDeployment_createDeletePolicy(t *testing.T) {
t.Parallel()
randStr := acctest.RandString(t, 10)
deploymentName := "tf-dm-" + randStr
accountId := "tf-dm-" + randStr
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckDeploymentManagerDeploymentDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccDeploymentManagerDeployment_createDeletePolicy(deploymentName, accountId),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
},
})
}
func TestAccDeploymentManagerDeployment_imports(t *testing.T) {
t.Parallel()
randStr := acctest.RandString(t, 10)
deploymentName := "tf-dm-" + randStr
accountId := "tf-dm-" + randStr
importFilepath := createYamlConfigFileForTest(t, "test-fixtures/service_account.yml.tmpl", map[string]interface{}{
"account_id": "{{ env['name'] }}",
})
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckDeploymentManagerDeploymentDestroyProducer(t),
testDeploymentManagerDeploymentVerifyServiceAccountMissing(t, accountId)),
Steps: []resource.TestStep{
{
Config: testAccDeploymentManagerDeployment_imports(deploymentName, accountId, importFilepath),
Check: testDeploymentManagerDeploymentVerifyServiceAccountExists(t, accountId),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
},
})
}
func TestAccDeploymentManagerDeployment_update(t *testing.T) {
t.Parallel()
randStr := acctest.RandString(t, 10)
deploymentName := "tf-dm-" + randStr
accountId := "tf-dm-first" + randStr
accountId2 := "tf-dm-second" + randStr
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckDeploymentManagerDeploymentDestroyProducer(t),
testDeploymentManagerDeploymentVerifyServiceAccountMissing(t, accountId)),
Steps: []resource.TestStep{
{
Config: testAccDeploymentManagerDeployment_preview(deploymentName, accountId),
Check: testDeploymentManagerDeploymentVerifyServiceAccountMissing(t, accountId),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
{
Config: testAccDeploymentManagerDeployment_previewUpdated(deploymentName, accountId2),
Check: testDeploymentManagerDeploymentVerifyServiceAccountMissing(t, accountId2),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
{
// Turn preview to false
Config: testAccDeploymentManagerDeployment_deployed(deploymentName, accountId),
Check: testDeploymentManagerDeploymentVerifyServiceAccountExists(t, accountId),
},
{
ResourceName: "google_deployment_manager_deployment.deployment",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"target", "create_policy", "delete_policy", "preview"},
},
},
})
}
func testAccDeploymentManagerDeployment_basicFile(deploymentName, yamlPath string) string {
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
target {
config {
content = file("%s")
}
}
labels {
key = "foo"
value = "bar"
}
}
`, deploymentName, yamlPath)
}
func testAccDeploymentManagerDeployment_invalidCreatePolicy(deployment, accountId string) string {
// The service account doesn't exist, so create policy acquire fails
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
create_policy = "ACQUIRE"
target {
config {
content = <<EOF
resources:
- name: %s
type: iam.v1.serviceAccount
properties:
accountId: %s
displayName: Test service account created by a DM Deployment, created in Terraform
EOF
}
}
}
`, deployment, accountId, accountId)
}
// NOTE: This is not recommended for use as actual Terraform config.
// This is just meant to test non-default createPolicy/deletePolicy parameters, but
// users shouldn't be managing resources in both Terraform and DM.
func testAccDeploymentManagerDeployment_createDeletePolicy(deployment, accountId string) string {
return fmt.Sprintf(`
resource google_service_account "deployment_account" {
account_id = "%s"
display_name = "test account for Terraform DeploymentManager deployment"
}
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
// Deployment Manager will not create or delete resources
create_policy = "ACQUIRE"
delete_policy = "ABANDON"
target {
config {
content = <<EOF
resources:
- name: "${google_service_account.deployment_account.account_id}"
type: iam.v1.serviceAccount
properties:
accountId: "${google_service_account.deployment_account.account_id}"
displayName: "${google_service_account.deployment_account.display_name}"
EOF
}
}
}
`, deployment, accountId)
}
func testAccDeploymentManagerDeployment_imports(deployment, accountId, importYamlPath string) string {
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
target {
config {
content = <<EOF
imports:
- path: service_account.jinja
resources:
- name: %s
type: service_account.jinja
EOF
}
imports {
name = "service_account.jinja"
content = file("%s")
}
}
}
`, deployment, accountId, importYamlPath)
}
func testAccDeploymentManagerDeployment_preview(deployment, accountId string) string {
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
preview = true
target {
config {
content = <<EOF
resources:
- name: %s
type: iam.v1.serviceAccount
properties:
accountId: %s
displayName: Test service account created by a DM Deployment, created in Terraform
EOF
}
}
labels {
key = "foo"
value = "one"
}
}
`, deployment, accountId, accountId)
}
func testAccDeploymentManagerDeployment_previewUpdated(deployment, accountId string) string {
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
preview = true
target {
config {
content = <<EOF
resources:
- name: %s
type: iam.v1.serviceAccount
properties:
accountId: %s
displayName: Test service account created by a Terraform DeploymentManager Deployment
EOF
}
}
labels {
key = "foo"
value = "one"
}
labels {
key = "bar"
value = "two"
}
}
`, deployment, accountId, accountId)
}
func testAccDeploymentManagerDeployment_deployed(deployment, accountId string) string {
return fmt.Sprintf(`
resource "google_deployment_manager_deployment" "deployment" {
name = "%s"
target {
config {
content = <<EOF
resources:
- name: %s
type: iam.v1.serviceAccount
properties:
accountId: %s
displayName: Test service account created by a DM Deployment, created in Terraform
EOF
}
}
labels {
key = "foo"
value = "one"
}
}
`, deployment, accountId, accountId)
}
func testDeploymentManagerDeploymentVerifyServiceAccountMissing(t *testing.T, accountId string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := acctest.GoogleProviderConfig(t)
exists, err := testCheckDeploymentServiceAccountExists(accountId, config)
if err != nil {
return err
}
if exists {
return fmt.Errorf("service account %s found, should not exist", accountId)
}
return nil
}
}
func testDeploymentManagerDeploymentVerifyServiceAccountExists(t *testing.T, accountId string) resource.TestCheckFunc {
return func(s *terraform.State) error {
config := acctest.GoogleProviderConfig(t)
exists, err := testCheckDeploymentServiceAccountExists(accountId, config)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("service account %s not found", accountId)
}
return nil
}
}
func testCheckDeploymentServiceAccountExists(accountId string, config *transport_tpg.Config) (exists bool, err error) {
_, err = config.NewIamClient(config.UserAgent).Projects.ServiceAccounts.Get(
fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", envvar.GetTestProjectFromEnv(), accountId, envvar.GetTestProjectFromEnv())).Do()
if err != nil {
if transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
return false, nil
}
return false, fmt.Errorf("unexpected error while trying to confirm deployment service account %q exists: %v", accountId, err)
}
return true, nil
}
func testAccCheckDeploymentManagerDestroyInvalidDeployment(t *testing.T, deploymentName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type == "google_deployment_manager_deployment" {
return fmt.Errorf("unexpected invalid deployment %q was saved in state", name)
}
}
config := acctest.GoogleProviderConfig(t)
url := fmt.Sprintf("%sprojects/%s/global/deployments/%s", config.DeploymentManagerBasePath, envvar.GetTestProjectFromEnv(), deploymentName)
_, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
RawURL: url,
UserAgent: config.UserAgent,
})
if !transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
return fmt.Errorf("Unexpected error while trying to confirm DeploymentManagerDeployment deleted: %v", err)
}
if err == nil {
return fmt.Errorf("DeploymentManagerDeployment still exists at %s", url)
}
return nil
}
}
func testAccCheckDeploymentManagerDeploymentDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_deployment_manager_deployment" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}
config := acctest.GoogleProviderConfig(t)
url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{DeploymentManagerBasePath}}projects/{{project}}/global/deployments/{{name}}")
if err != nil {
return err
}
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
RawURL: url,
UserAgent: config.UserAgent,
})
if err == nil {
return fmt.Errorf("DeploymentManagerDeployment still exists at %s", url)
}
}
return nil
}
}
func createYamlConfigFileForTest(t *testing.T, sourcePath string, context map[string]interface{}) string {
source, err := ioutil.ReadFile(sourcePath)
if err != nil {
t.Fatal(err.Error())
}
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
buf.WriteString(acctest.Nprintf(string(source), context))
// Create temp file to write zip to
tmpfile, err := ioutil.TempFile("", "*.yml")
if err != nil {
t.Fatal(err.Error())
}
if _, err := tmpfile.Write(buf.Bytes()); err != nil {
t.Fatal(err.Error())
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err.Error())
}
return tmpfile.Name()
}