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.
This commit is contained in:
parent
927085289d
commit
72ccf22a92
|
@ -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,21 +43,88 @@ 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,
|
||||
"project": projectID,
|
||||
"bucket": strings.ToLower(t.Name()),
|
||||
"prefix": "",
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue