diff --git a/backend/remote-state/gcs/backend_test.go b/backend/remote-state/gcs/backend_test.go index 3ccb645f6..9f937db22 100644 --- a/backend/remote-state/gcs/backend_test.go +++ b/backend/remote-state/gcs/backend_test.go @@ -1,7 +1,9 @@ package gcs import ( + "fmt" "os" + "strings" "testing" "github.com/hashicorp/terraform/backend" @@ -9,6 +11,8 @@ import ( ) func TestStateFile(t *testing.T) { + t.Parallel() + cases := []struct { prefix string defaultStateFile string @@ -39,22 +43,89 @@ func TestStateFile(t *testing.T) { } } -func TestGCSBackend(t *testing.T) { - // This test creates a bucket in GCS and populates it. - // It may incur costs, so it will only run if the GOOGLE_PROJECT - // environment variable is set. +func TestRemoteClient(t *testing.T) { + t.Parallel() + + be := testBackend(t) + + ss, err := be.State(backend.DefaultStateName) + if err != nil { + t.Fatalf("be.State(%q) = %v", backend.DefaultStateName, err) + } + + rs, ok := ss.(*remote.State) + if !ok { + t.Fatalf("be.State(): got a %T, want a *remote.State", ss) + } + + remote.TestClient(t, rs.Client) + + cleanBackend(t, be) +} + +func TestRemoteLocks(t *testing.T) { + t.Parallel() + + be := testBackend(t) + + remoteClient := func() (remote.Client, error) { + ss, err := be.State(backend.DefaultStateName) + if err != nil { + return nil, err + } + + rs, ok := ss.(*remote.State) + if !ok { + return nil, fmt.Errorf("be.State(): got a %T, want a *remote.State", ss) + } + + return rs.Client, nil + } + + c0, err := remoteClient() + if err != nil { + t.Fatalf("remoteClient(0) = %v", err) + } + c1, err := remoteClient() + if err != nil { + t.Fatalf("remoteClient(1) = %v", err) + } + + remote.TestRemoteLocks(t, c0, c1) + + cleanBackend(t, be) +} + +func TestBackend(t *testing.T) { + t.Parallel() + + be0 := testBackend(t) + be1 := testBackend(t) + + // clean up all states left behind by previous runs -- + // backend.TestBackend() will complain about any non-default states. + cleanBackend(t, be0) + + backend.TestBackend(t, be0, be1) + + cleanBackend(t, be0) +} + +// testBackend returns a new GCS backend. +// This creates a bucket in GCS and populates it. Since this may incur costs, +// it will only run if the GOOGLE_PROJECT environment variable is set. +func testBackend(t *testing.T) backend.Backend { + t.Helper() projectID := os.Getenv("GOOGLE_PROJECT") if projectID == "" { - t.Skipf("skipping; set GOOGLE_PROJECT to activate") + t.Skip("skipping; set GOOGLE_PROJECT to activate") } - const bucketName = "terraform_remote-state_test" - t.Logf("using bucket %q in project %q", bucketName, projectID) - config := map[string]interface{}{ - "bucket": bucketName, - "prefix": "", + "project": projectID, + "bucket": strings.ToLower(t.Name()), + "prefix": "", } if creds := os.Getenv("GOOGLE_CREDENTIALS"); creds != "" { @@ -64,63 +135,23 @@ func TestGCSBackend(t *testing.T) { t.Log("using default credentials; set GOOGLE_CREDENTIALS for custom credentials") } - be := backend.TestBackendConfig(t, New(), config) + return backend.TestBackendConfig(t, New(), config) +} - gcsBE, ok := be.(*gcsBackend) - if !ok { - t.Fatalf("backend: got %T, want *gcsBackend", be) - } - - ctx := gcsBE.storageContext - - // create a new bucket and error out if we can't, e.g. because it already exists. - if err := gcsBE.storageClient.Bucket(bucketName).Create(ctx, projectID, nil); err != nil { - t.Fatalf("creating bucket failed: %v", err) - } - t.Log("bucket has been created") - - defer func() { - if err := gcsBE.storageClient.Bucket(bucketName).Delete(ctx); err != nil { - t.Errorf("deleting bucket failed: %v", err) - } else { - t.Log("bucket has been deleted") - } - }() - - // this should create a new state file - _, err := be.State("TestGCSBackend") - if err != nil { - t.Fatalf("State(\"TestGCSBackend\"): %v", err) - } +// cleanBackend deletes all states from be except the default state. +func cleanBackend(t *testing.T, be backend.Backend) { + t.Helper() states, err := be.States() if err != nil { - t.Fatalf("States(): %v", err) + t.Fatalf("be.States() = %v; manual clean-up may be required", err) } - - found := false for _, st := range states { - if st == "TestGCSBackend" { - found = true - break + if st == backend.DefaultStateName { + continue + } + if err := be.DeleteState(st); err != nil { + t.Fatalf("be.DeleteState(%q) = %v; manual clean-up may be required", st, err) } } - if !found { - t.Errorf("be.States() = %#v, missing \"TestGCSBackend\"", states) - } - - // ensure state file exists - if _, err := gcsBE.storageClient.Bucket(bucketName).Object("TestGCSBackend.tfstate").Attrs(ctx); err != nil { - t.Fatalf("Attrs(\"TestGCSBackend.tfstate\"): %v", err) - } - - c, err := gcsBE.client("TestGCSBackend_remote_TestClient") - if err != nil { - t.Fatal(err) - } - remote.TestClient(t, c) - - if err := be.DeleteState("TestGCSBackend"); err != nil { - t.Errorf("DeleteState(\"TestGCSBackend\"): %v", err) - } }