mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-07 03:01:06 +00:00
7c643593c6
Signed-off-by: Modular Magician <magic-modules@google.com>
205 lines
6.7 KiB
Go
205 lines
6.7 KiB
Go
package google
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"testing"
|
|
|
|
"google.golang.org/api/cloudkms/v1"
|
|
"google.golang.org/api/iam/v1"
|
|
)
|
|
|
|
var SharedKeyRing = "tftest-shared-keyring-1"
|
|
var SharedCyptoKey = "tftest-shared-key-1"
|
|
|
|
type bootstrappedKMS struct {
|
|
*cloudkms.KeyRing
|
|
*cloudkms.CryptoKey
|
|
}
|
|
|
|
// BootstrapKMSKey returns a KMS key in the "global" location.
|
|
// See BootstrapKMSKeyInLocation.
|
|
func BootstrapKMSKey(t *testing.T) bootstrappedKMS {
|
|
return BootstrapKMSKeyInLocation(t, "global")
|
|
}
|
|
|
|
/**
|
|
* BootstrapKMSKeyWithLocation will return a KMS key in a particular location
|
|
* that can be used in tests that are testing KMS integration with other resources.
|
|
*
|
|
* This will either return an existing key or create one if it hasn't been created
|
|
* in the project yet. The motivation is because keyrings don't get deleted and we
|
|
* don't want a linear growth of disabled keyrings in a project. We also don't want
|
|
* to incur the overhead of creating a new project for each test that needs to use
|
|
* a KMS key.
|
|
**/
|
|
func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS {
|
|
if v := os.Getenv("TF_ACC"); v == "" {
|
|
log.Println("Acceptance tests and bootstrapping skipped unless env 'TF_ACC' set")
|
|
|
|
// If not running acceptance tests, return an empty object
|
|
return bootstrappedKMS{
|
|
&cloudkms.KeyRing{},
|
|
&cloudkms.CryptoKey{},
|
|
}
|
|
}
|
|
|
|
projectID := getTestProjectFromEnv()
|
|
keyRingParent := fmt.Sprintf("projects/%s/locations/%s", projectID, locationID)
|
|
keyRingName := fmt.Sprintf("%s/keyRings/%s", keyRingParent, SharedKeyRing)
|
|
keyParent := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", projectID, locationID, SharedKeyRing)
|
|
keyName := fmt.Sprintf("%s/cryptoKeys/%s", keyParent, SharedCyptoKey)
|
|
|
|
config := Config{
|
|
Credentials: getTestCredsFromEnv(),
|
|
Project: getTestProjectFromEnv(),
|
|
Region: getTestRegionFromEnv(),
|
|
Zone: getTestZoneFromEnv(),
|
|
}
|
|
|
|
if err := config.LoadAndValidate(); err != nil {
|
|
t.Errorf("Unable to bootstrap KMS key: %s", err)
|
|
}
|
|
|
|
// Get or Create the hard coded shared keyring for testing
|
|
kmsClient := config.clientKms
|
|
keyRing, err := kmsClient.Projects.Locations.KeyRings.Get(keyRingName).Do()
|
|
if err != nil {
|
|
if isGoogleApiErrorWithCode(err, 404) {
|
|
keyRing, err = kmsClient.Projects.Locations.KeyRings.Create(keyRingParent, &cloudkms.KeyRing{}).
|
|
KeyRingId(SharedKeyRing).Do()
|
|
if err != nil {
|
|
t.Errorf("Unable to bootstrap KMS key. Cannot create keyRing: %s", err)
|
|
}
|
|
} else {
|
|
t.Errorf("Unable to bootstrap KMS key. Cannot retrieve keyRing: %s", err)
|
|
}
|
|
}
|
|
|
|
if keyRing == nil {
|
|
t.Fatalf("Unable to bootstrap KMS key. keyRing is nil!")
|
|
}
|
|
|
|
// Get or Create the hard coded, shared crypto key for testing
|
|
cryptoKey, err := kmsClient.Projects.Locations.KeyRings.CryptoKeys.Get(keyName).Do()
|
|
if err != nil {
|
|
if isGoogleApiErrorWithCode(err, 404) {
|
|
newKey := cloudkms.CryptoKey{
|
|
Purpose: "ENCRYPT_DECRYPT",
|
|
}
|
|
|
|
cryptoKey, err = kmsClient.Projects.Locations.KeyRings.CryptoKeys.Create(keyParent, &newKey).
|
|
CryptoKeyId(SharedCyptoKey).Do()
|
|
if err != nil {
|
|
t.Errorf("Unable to bootstrap KMS key. Cannot create new CryptoKey: %s", err)
|
|
}
|
|
|
|
} else {
|
|
t.Errorf("Unable to bootstrap KMS key. Cannot call CryptoKey service: %s", err)
|
|
}
|
|
}
|
|
|
|
if cryptoKey == nil {
|
|
t.Fatalf("Unable to bootstrap KMS key. CryptoKey is nil!")
|
|
}
|
|
|
|
return bootstrappedKMS{
|
|
keyRing,
|
|
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
|
|
}
|