From 72ccf22a9203e46e09ecd332c3fc8a3009881c1a Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Tue, 26 Sep 2017 09:20:43 +0200 Subject: [PATCH] backend/remote-state/gcs: Implement additional tests. This calls backend.TestBackend() and remote.TestRemoteLocks() for standardized acceptance tests. It removes custom listing tests since those are performed by backend.TestBackend(), too. Since each tests uses its own bucket, all tests can be run in parallel. --- backend/remote-state/gcs/backend_test.go | 153 ++++++++++++++--------- 1 file changed, 92 insertions(+), 61 deletions(-) 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) - } }