Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrap service accounts for impersonation tests #582

Merged
merged 1 commit into from
May 2, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions google-beta/bootstrap_utils_test.go
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import (
"testing"

"google.golang.org/api/cloudkms/v1"
"google.golang.org/api/iam/v1"
)

var SharedKeyRing = "tftest-shared-keyring-1"
@@ -103,3 +104,96 @@ func BootstrapKMSKey(t *testing.T) bootstrappedKMS {
cryptoKey,
}
}

var serviceAccountEmail = "tf-bootstrap-service-account"
var serviceAccountDisplay = "Bootstrapped Service Account for Terraform tests"

// Some tests need a second service account, other than the test runner, to assert functionality on.
// This provides a well-known service account that can be used when dynamically creating a service
// account isn't an option.
func getOrCreateServiceAccount(config Config, project string) (*iam.ServiceAccount, error) {
name := fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", project, serviceAccountEmail, project)
log.Printf("[DEBUG] Verifying %s as bootstrapped service account.\n", name)

sa, err := config.clientIAM.Projects.ServiceAccounts.Get(name).Do()
if err != nil && !isGoogleApiErrorWithCode(err, 404) {
return nil, err
}

if sa == nil {
log.Printf("[DEBUG] Account missing. Creating %s as bootstrapped service account.\n", name)
sa = &iam.ServiceAccount{
DisplayName: serviceAccountDisplay,
}

r := &iam.CreateServiceAccountRequest{
AccountId: serviceAccountEmail,
ServiceAccount: sa,
}
sa, err = config.clientIAM.Projects.ServiceAccounts.Create("projects/"+project, r).Do()
if err != nil {
return nil, err
}
}

return sa, nil
}

// In order to test impersonation we need to grant the testRunner's account the ability to grant tokens
// on a different service account. Granting permissions takes time and there is no operation to wait on
// so instead this creates a single service account once per test-suite with the correct permissions.
// The first time this test is run it will fail, but subsequent runs will succeed.
func impersonationServiceAccountPermissions(config Config, sa *iam.ServiceAccount, testRunner string) error {
log.Printf("[DEBUG] Setting service account permissions.\n")
policy := iam.Policy{
Bindings: []*iam.Binding{},
}

binding := &iam.Binding{
Role: "roles/iam.serviceAccountTokenCreator",
Members: []string{"serviceAccount:" + sa.Email, "serviceAccount:" + testRunner},
}
policy.Bindings = append(policy.Bindings, binding)

// Overwrite the roles each time on this service account. This is because this account is
// only created for the test suite and will stop snowflaking of permissions to get tests
// to run. Overwriting permissions on 1 service account shouldn't affect others.
_, err := config.clientIAM.Projects.ServiceAccounts.SetIamPolicy(sa.Name, &iam.SetIamPolicyRequest{
Policy: &policy,
}).Do()
if err != nil {
return err
}

return nil
}

func BootstrapServiceAccount(t *testing.T, project, testRunner string) string {
if v := os.Getenv("TF_ACC"); v == "" {
log.Println("Acceptance tests and bootstrapping skipped unless env 'TF_ACC' set")
return ""
}

config := Config{
Credentials: getTestCredsFromEnv(),
Project: getTestProjectFromEnv(),
Region: getTestRegionFromEnv(),
Zone: getTestZoneFromEnv(),
}

if err := config.LoadAndValidate(); err != nil {
t.Fatalf("Bootstrapping failed. Unable to load test config: %s", err)
}

sa, err := getOrCreateServiceAccount(config, project)
if err != nil {
t.Fatalf("Bootstrapping failed. Cannot retrieve service account, %s", err)
}

err = impersonationServiceAccountPermissions(config, sa, testRunner)
if err != nil {
t.Fatalf("Bootstrapping failed. Cannot set service account permissions, %s", err)
}

return sa.Email
}
Original file line number Diff line number Diff line change
@@ -30,8 +30,8 @@ func TestAccDataSourceGoogleServiceAccountAccessToken_basic(t *testing.T) {
t.Parallel()

resourceName := "data.google_service_account_access_token.default"

targetServiceAccountEmail := getTestServiceAccountFromEnv(t)
serviceAccount := getTestServiceAccountFromEnv(t)
targetServiceAccountEmail := BootstrapServiceAccount(t, getTestProjectFromEnv(), serviceAccount)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },