mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-04 17:51:11 +00:00
43d40f4065
<!-- This change is generated by MagicModules. --> /cc @chrisst
507 lines
13 KiB
Go
507 lines
13 KiB
Go
package google
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/helper/acctest"
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"google.golang.org/api/cloudresourcemanager/v1"
|
|
)
|
|
|
|
// Test that an IAM policy can be applied to a project
|
|
func TestAccProjectIamPolicy_basic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
org := getTestOrgFromEnv(t)
|
|
pid := "terraform-" + acctest.RandString(10)
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
Providers: testAccProviders,
|
|
Steps: []resource.TestStep{
|
|
// Create a new project
|
|
{
|
|
Config: testAccProject_create(pid, pname, org),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
testAccProjectExistingPolicy(pid),
|
|
),
|
|
},
|
|
// Apply an IAM policy from a data source. The application
|
|
// merges policies, so we validate the expected state.
|
|
{
|
|
Config: testAccProjectAssociatePolicyBasic(pid, pname, org),
|
|
},
|
|
{
|
|
ResourceName: "google_project_iam_policy.acceptance",
|
|
ImportState: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test that a non-collapsed IAM policy doesn't perpetually diff
|
|
func TestAccProjectIamPolicy_expanded(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
org := getTestOrgFromEnv(t)
|
|
pid := "terraform-" + acctest.RandString(10)
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
Providers: testAccProviders,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: testAccProjectAssociatePolicyExpanded(pid, pname, org),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
testAccCheckGoogleProjectIamPolicyExists("google_project_iam_policy.acceptance", "data.google_iam_policy.expanded", pid),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test that an IAM policy with an audit config can be applied to a project
|
|
func TestAccProjectIamPolicy_basicAuditConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
org := getTestOrgFromEnv(t)
|
|
pid := "tf-acctest-" + acctest.RandString(10)
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
Providers: testAccProviders,
|
|
Steps: []resource.TestStep{
|
|
// Create a new project
|
|
{
|
|
Config: testAccProject_create(pid, pname, org),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
testAccProjectExistingPolicy(pid),
|
|
),
|
|
},
|
|
// Apply an IAM policy from a data source. The application
|
|
// merges policies, so we validate the expected state.
|
|
{
|
|
Config: testAccProjectAssociatePolicyAuditConfigBasic(pid, pname, org),
|
|
},
|
|
{
|
|
ResourceName: "google_project_iam_policy.acceptance",
|
|
ImportState: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test that a non-collapsed IAM policy with AuditConfig doesn't perpetually diff
|
|
func TestAccProjectIamPolicy_expandedAuditConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
org := getTestOrgFromEnv(t)
|
|
pid := "tf-acctest-" + acctest.RandString(10)
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
Providers: testAccProviders,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: testAccProjectAssociatePolicyAuditConfigExpanded(pid, pname, org),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
testAccCheckGoogleProjectIamPolicyExists("google_project_iam_policy.acceptance", "data.google_iam_policy.expanded", pid),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func getStatePrimaryResource(s *terraform.State, res, expectedID string) (*terraform.InstanceState, error) {
|
|
// Get the project resource
|
|
resource, ok := s.RootModule().Resources[res]
|
|
if !ok {
|
|
return nil, fmt.Errorf("Not found: %s", res)
|
|
}
|
|
if resource.Primary.Attributes["id"] != expectedID && expectedID != "" {
|
|
return nil, fmt.Errorf("Expected project %q to match ID %q in state", resource.Primary.ID, expectedID)
|
|
}
|
|
return resource.Primary, nil
|
|
}
|
|
|
|
func getGoogleProjectIamPolicyFromResource(resource *terraform.InstanceState) (cloudresourcemanager.Policy, error) {
|
|
var p cloudresourcemanager.Policy
|
|
ps, ok := resource.Attributes["policy_data"]
|
|
if !ok {
|
|
return p, fmt.Errorf("Resource %q did not have a 'policy_data' attribute. Attributes were %#v", resource.ID, resource.Attributes)
|
|
}
|
|
if err := json.Unmarshal([]byte(ps), &p); err != nil {
|
|
return p, fmt.Errorf("Could not unmarshal %s:\n: %v", ps, err)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func getGoogleProjectIamPolicyFromState(s *terraform.State, res, expectedID string) (cloudresourcemanager.Policy, error) {
|
|
project, err := getStatePrimaryResource(s, res, expectedID)
|
|
if err != nil {
|
|
return cloudresourcemanager.Policy{}, err
|
|
}
|
|
return getGoogleProjectIamPolicyFromResource(project)
|
|
}
|
|
|
|
func testAccCheckGoogleProjectIamPolicyExists(projectRes, policyRes, pid string) resource.TestCheckFunc {
|
|
return func(s *terraform.State) error {
|
|
projectPolicy, err := getGoogleProjectIamPolicyFromState(s, projectRes, pid)
|
|
if err != nil {
|
|
return fmt.Errorf("Error retrieving IAM policy for project from state: %s", err)
|
|
}
|
|
policyPolicy, err := getGoogleProjectIamPolicyFromState(s, policyRes, "")
|
|
if err != nil {
|
|
return fmt.Errorf("Error retrieving IAM policy for data_policy from state: %s", err)
|
|
}
|
|
|
|
// The bindings in both policies should be identical
|
|
if !compareBindings(projectPolicy.Bindings, policyPolicy.Bindings) {
|
|
return fmt.Errorf("Project and data source policies do not match: project policy is %+v, data resource policy is %+v", derefBindings(projectPolicy.Bindings), derefBindings(policyPolicy.Bindings))
|
|
}
|
|
|
|
// The audit configs in both policies should be identical
|
|
if !compareAuditConfigs(projectPolicy.AuditConfigs, policyPolicy.AuditConfigs) {
|
|
return fmt.Errorf("Project and data source policies do not match: project policy is %+v, data resource policy is %+v", projectPolicy.AuditConfigs, policyPolicy.AuditConfigs)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func TestIamOverwriteBinding(t *testing.T) {
|
|
table := []struct {
|
|
input []*cloudresourcemanager.Binding
|
|
override cloudresourcemanager.Binding
|
|
expect []cloudresourcemanager.Binding
|
|
}{
|
|
{
|
|
input: []*cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2"},
|
|
},
|
|
},
|
|
override: cloudresourcemanager.Binding{
|
|
Role: "role-1",
|
|
Members: []string{"new-member"},
|
|
},
|
|
expect: []cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"new-member"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: []*cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2"},
|
|
},
|
|
},
|
|
override: cloudresourcemanager.Binding{
|
|
Role: "role-2",
|
|
Members: []string{"member-3"},
|
|
},
|
|
expect: []cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2"},
|
|
},
|
|
{
|
|
Role: "role-2",
|
|
Members: []string{"member-3"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range table {
|
|
got := overwriteBinding(test.input, &test.override)
|
|
if !reflect.DeepEqual(derefBindings(got), test.expect) {
|
|
t.Errorf("OverwriteIamBinding failed.\nGot %+v\nWant %+v", derefBindings(got), test.expect)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIamMergeBindings(t *testing.T) {
|
|
table := []struct {
|
|
input []*cloudresourcemanager.Binding
|
|
expect []cloudresourcemanager.Binding
|
|
}{
|
|
{
|
|
input: []*cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2"},
|
|
},
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-3"},
|
|
},
|
|
},
|
|
expect: []cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2", "member-3"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: []*cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-3", "member-4"},
|
|
},
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-2", "member-1"},
|
|
},
|
|
{
|
|
Role: "role-2",
|
|
Members: []string{"member-1"},
|
|
},
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-5"},
|
|
},
|
|
{
|
|
Role: "role-3",
|
|
Members: []string{"member-1"},
|
|
},
|
|
{
|
|
Role: "role-2",
|
|
Members: []string{"member-2"},
|
|
},
|
|
{Role: "empty-role", Members: []string{}},
|
|
},
|
|
expect: []cloudresourcemanager.Binding{
|
|
{
|
|
Role: "role-1",
|
|
Members: []string{"member-1", "member-2", "member-3", "member-4", "member-5"},
|
|
},
|
|
{
|
|
Role: "role-2",
|
|
Members: []string{"member-1", "member-2"},
|
|
},
|
|
{
|
|
Role: "role-3",
|
|
Members: []string{"member-1"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, test := range table {
|
|
got := mergeBindings(test.input)
|
|
sort.Sort(sortableBindings(got))
|
|
for i := range got {
|
|
sort.Strings(got[i].Members)
|
|
}
|
|
if !reflect.DeepEqual(derefBindings(got), test.expect) {
|
|
t.Errorf("\ngot %+v\nexpected %+v", derefBindings(got), test.expect)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Confirm that a project has an IAM policy with at least 1 binding
|
|
func testAccProjectExistingPolicy(pid string) resource.TestCheckFunc {
|
|
return func(s *terraform.State) error {
|
|
c := testAccProvider.Meta().(*Config)
|
|
var err error
|
|
originalPolicy, err = getProjectIamPolicy(pid, c)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to retrieve IAM Policy for project %q: %s", pid, err)
|
|
}
|
|
if len(originalPolicy.Bindings) == 0 {
|
|
return fmt.Errorf("Refuse to run test against project with zero IAM Bindings. This is likely an error in the test code that is not properly identifying the IAM policy of a project.")
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func testAccProjectAssociatePolicyBasic(pid, name, org string) string {
|
|
return fmt.Sprintf(`
|
|
resource "google_project" "acceptance" {
|
|
project_id = "%s"
|
|
name = "%s"
|
|
org_id = "%s"
|
|
}
|
|
|
|
resource "google_project_iam_policy" "acceptance" {
|
|
project = "${google_project.acceptance.id}"
|
|
policy_data = "${data.google_iam_policy.admin.policy_data}"
|
|
}
|
|
|
|
data "google_iam_policy" "admin" {
|
|
binding {
|
|
role = "roles/storage.objectViewer"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
]
|
|
}
|
|
binding {
|
|
role = "roles/compute.instanceAdmin"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
"user:evandbrown@gmail.com",
|
|
]
|
|
}
|
|
}
|
|
`, pid, name, org)
|
|
}
|
|
|
|
func testAccProjectAssociatePolicyAuditConfigBasic(pid, name, org string) string {
|
|
return fmt.Sprintf(`
|
|
resource "google_project" "acceptance" {
|
|
project_id = "%s"
|
|
name = "%s"
|
|
org_id = "%s"
|
|
}
|
|
|
|
resource "google_project_iam_policy" "acceptance" {
|
|
project = "${google_project.acceptance.id}"
|
|
policy_data = "${data.google_iam_policy.admin.policy_data}"
|
|
}
|
|
|
|
data "google_iam_policy" "admin" {
|
|
binding {
|
|
role = "roles/storage.objectViewer"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
]
|
|
}
|
|
binding {
|
|
role = "roles/compute.instanceAdmin"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
"user:evandbrown@gmail.com",
|
|
]
|
|
}
|
|
audit_config {
|
|
service = "cloudkms.googleapis.com"
|
|
audit_log_configs = [
|
|
{
|
|
log_type = "DATA_READ"
|
|
exempted_members = [
|
|
"user:paddy@hashicorp.com",
|
|
]
|
|
},
|
|
{
|
|
log_type = "DATA_WRITE"
|
|
}
|
|
]
|
|
}
|
|
audit_config {
|
|
service = "cloudsql.googleapis.com"
|
|
audit_log_configs = [
|
|
{
|
|
log_type = "DATA_READ"
|
|
exempted_members = [
|
|
"user:paddy@hashicorp.com",
|
|
]
|
|
},
|
|
{
|
|
log_type = "DATA_WRITE"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
`, pid, name, org)
|
|
}
|
|
|
|
func testAccProject_create(pid, name, org string) string {
|
|
return fmt.Sprintf(`
|
|
resource "google_project" "acceptance" {
|
|
project_id = "%s"
|
|
name = "%s"
|
|
org_id = "%s"
|
|
}`, pid, name, org)
|
|
}
|
|
|
|
func testAccProjectAssociatePolicyExpanded(pid, name, org string) string {
|
|
return fmt.Sprintf(`
|
|
resource "google_project" "acceptance" {
|
|
project_id = "%s"
|
|
name = "%s"
|
|
org_id = "%s"
|
|
}
|
|
resource "google_project_iam_policy" "acceptance" {
|
|
project = "${google_project.acceptance.id}"
|
|
policy_data = "${data.google_iam_policy.expanded.policy_data}"
|
|
}
|
|
|
|
data "google_iam_policy" "expanded" {
|
|
binding {
|
|
role = "roles/viewer"
|
|
members = [
|
|
"user:paddy@carvers.co",
|
|
]
|
|
}
|
|
|
|
binding {
|
|
role = "roles/viewer"
|
|
members = [
|
|
"user:paddy@hashicorp.com",
|
|
]
|
|
}
|
|
}`, pid, name, org)
|
|
}
|
|
|
|
func testAccProjectAssociatePolicyAuditConfigExpanded(pid, name, org string) string {
|
|
return fmt.Sprintf(`
|
|
resource "google_project" "acceptance" {
|
|
project_id = "%s"
|
|
name = "%s"
|
|
org_id = "%s"
|
|
}
|
|
resource "google_project_iam_policy" "acceptance" {
|
|
project = "${google_project.acceptance.id}"
|
|
policy_data = "${data.google_iam_policy.expanded.policy_data}"
|
|
}
|
|
|
|
data "google_iam_policy" "expanded" {
|
|
binding {
|
|
role = "roles/storage.objectViewer"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
]
|
|
}
|
|
binding {
|
|
role = "roles/compute.instanceAdmin"
|
|
members = [
|
|
"user:evanbrown@google.com",
|
|
"user:evandbrown@gmail.com",
|
|
]
|
|
}
|
|
audit_config {
|
|
service = "cloudkms.googleapis.com"
|
|
audit_log_configs = [
|
|
{
|
|
log_type = "DATA_READ"
|
|
exempted_members = [
|
|
"user:paddy@hashicorp.com",
|
|
]
|
|
},
|
|
{
|
|
log_type = "DATA_WRITE"
|
|
}
|
|
]
|
|
}
|
|
audit_config {
|
|
service = "cloudkms.googleapis.com"
|
|
audit_log_configs = [
|
|
{
|
|
log_type = "DATA_READ"
|
|
exempted_members = [
|
|
"user:paddy@carvers.co",
|
|
]
|
|
},
|
|
{
|
|
log_type = "ADMIN_READ"
|
|
}
|
|
]
|
|
}
|
|
}`, pid, name, org)
|
|
}
|