github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/backend/remote-state/gcs/backend_test.go (about)

     1  package gcs
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform/backend"
    10  	"github.com/hashicorp/terraform/state/remote"
    11  )
    12  
    13  func TestStateFile(t *testing.T) {
    14  	t.Parallel()
    15  
    16  	cases := []struct {
    17  		prefix           string
    18  		defaultStateFile string
    19  		name             string
    20  		wantStateFile    string
    21  		wantLockFile     string
    22  	}{
    23  		{"state", "", "default", "state/default.tfstate", "state/default.tflock"},
    24  		{"state", "", "test", "state/test.tfstate", "state/test.tflock"},
    25  		{"state", "legacy.tfstate", "default", "legacy.tfstate", "legacy.tflock"},
    26  		{"state", "legacy.tfstate", "test", "state/test.tfstate", "state/test.tflock"},
    27  		{"state", "legacy.state", "default", "legacy.state", "legacy.state.tflock"},
    28  		{"state", "legacy.state", "test", "state/test.tfstate", "state/test.tflock"},
    29  	}
    30  	for _, c := range cases {
    31  		b := &gcsBackend{
    32  			prefix:           c.prefix,
    33  			defaultStateFile: c.defaultStateFile,
    34  		}
    35  
    36  		if got := b.stateFile(c.name); got != c.wantStateFile {
    37  			t.Errorf("stateFile(%q) = %q, want %q", c.name, got, c.wantStateFile)
    38  		}
    39  
    40  		if got := b.lockFile(c.name); got != c.wantLockFile {
    41  			t.Errorf("lockFile(%q) = %q, want %q", c.name, got, c.wantLockFile)
    42  		}
    43  	}
    44  }
    45  
    46  func TestRemoteClient(t *testing.T) {
    47  	t.Parallel()
    48  
    49  	be := setupBackend(t)
    50  	defer teardownBackend(t, be)
    51  
    52  	ss, err := be.State(backend.DefaultStateName)
    53  	if err != nil {
    54  		t.Fatalf("be.State(%q) = %v", backend.DefaultStateName, err)
    55  	}
    56  
    57  	rs, ok := ss.(*remote.State)
    58  	if !ok {
    59  		t.Fatalf("be.State(): got a %T, want a *remote.State", ss)
    60  	}
    61  
    62  	remote.TestClient(t, rs.Client)
    63  }
    64  
    65  func TestRemoteLocks(t *testing.T) {
    66  	t.Parallel()
    67  
    68  	be := setupBackend(t)
    69  	defer teardownBackend(t, be)
    70  
    71  	remoteClient := func() (remote.Client, error) {
    72  		ss, err := be.State(backend.DefaultStateName)
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  
    77  		rs, ok := ss.(*remote.State)
    78  		if !ok {
    79  			return nil, fmt.Errorf("be.State(): got a %T, want a *remote.State", ss)
    80  		}
    81  
    82  		return rs.Client, nil
    83  	}
    84  
    85  	c0, err := remoteClient()
    86  	if err != nil {
    87  		t.Fatalf("remoteClient(0) = %v", err)
    88  	}
    89  	c1, err := remoteClient()
    90  	if err != nil {
    91  		t.Fatalf("remoteClient(1) = %v", err)
    92  	}
    93  
    94  	remote.TestRemoteLocks(t, c0, c1)
    95  }
    96  
    97  func TestBackend(t *testing.T) {
    98  	t.Parallel()
    99  
   100  	be0 := setupBackend(t)
   101  	defer teardownBackend(t, be0)
   102  
   103  	be1 := setupBackend(t)
   104  
   105  	backend.TestBackend(t, be0, be1)
   106  }
   107  
   108  // setupBackend returns a new GCS backend.
   109  func setupBackend(t *testing.T) backend.Backend {
   110  	t.Helper()
   111  
   112  	projectID := os.Getenv("GOOGLE_PROJECT")
   113  	if projectID == "" || os.Getenv("TF_ACC") == "" {
   114  		t.Skip("This test creates a bucket in GCS and populates it. " +
   115  			"Since this may incur costs, it will only run if " +
   116  			"the TF_ACC and GOOGLE_PROJECT environment variables are set.")
   117  	}
   118  
   119  	config := map[string]interface{}{
   120  		"project": projectID,
   121  		"bucket":  toBucketName(projectID + "-" + t.Name()),
   122  		"prefix":  "",
   123  	}
   124  
   125  	if creds := os.Getenv("GOOGLE_CREDENTIALS"); creds != "" {
   126  		config["credentials"] = creds
   127  		t.Logf("using credentials from %q", creds)
   128  	} else {
   129  		t.Log("using default credentials; set GOOGLE_CREDENTIALS for custom credentials")
   130  	}
   131  
   132  	return backend.TestBackendConfig(t, New(), config)
   133  }
   134  
   135  // teardownBackend deletes all states from be except the default state.
   136  func teardownBackend(t *testing.T, be backend.Backend) {
   137  	t.Helper()
   138  
   139  	// Delete all states. The bucket must be empty before it can be deleted.
   140  	states, err := be.States()
   141  	if err != nil {
   142  		t.Fatalf("be.States() = %v; manual clean-up may be required", err)
   143  	}
   144  	for _, st := range states {
   145  		if st == backend.DefaultStateName {
   146  			continue
   147  		}
   148  		if err := be.DeleteState(st); err != nil {
   149  			t.Fatalf("be.DeleteState(%q) = %v; manual clean-up may be required", st, err)
   150  		}
   151  	}
   152  
   153  	gcsBE, ok := be.(*gcsBackend)
   154  	if !ok {
   155  		t.Fatalf("be is a %T, want a *gcsBackend", be)
   156  	}
   157  	ctx := gcsBE.storageContext
   158  
   159  	// Delete the default state, which DeleteState() will refuse to do.
   160  	// It's okay if this fails, not all tests create a default state.
   161  	if err := gcsBE.storageClient.Bucket(gcsBE.bucketName).Object("default.tfstate").Delete(ctx); err != nil {
   162  		t.Logf("deleting \"default.tfstate\": %v; manual clean-up may be required", err)
   163  	}
   164  
   165  	// Delete the bucket itself.
   166  	if err := gcsBE.storageClient.Bucket(gcsBE.bucketName).Delete(ctx); err != nil {
   167  		t.Fatalf("deleting bucket failed: %v; manual cleanup may be required, though later test runs will happily reuse an existing bucket", err)
   168  	}
   169  }
   170  
   171  // toBucketName returns a copy of in that is suitable for use as a bucket name.
   172  // All upper case characters are converted to lower case, other invalid
   173  // characters are replaced by '_'.
   174  func toBucketName(in string) string {
   175  	// Bucket names must contain only lowercase letters, numbers, dashes
   176  	// (-), and underscores (_).
   177  	isValid := func(r rune) bool {
   178  		switch {
   179  		case r >= 'a' && r <= 'z':
   180  			return true
   181  		case r >= '0' && r <= '9':
   182  			return true
   183  		case r == '-' || r == '_':
   184  			return true
   185  		default:
   186  			return false
   187  
   188  		}
   189  	}
   190  
   191  	out := make([]rune, 0, len(in))
   192  	for _, r := range strings.ToLower(in) {
   193  		if !isValid(r) {
   194  			r = '_'
   195  		}
   196  		out = append(out, r)
   197  	}
   198  
   199  	// Bucket names must contain 3 to 63 characters.
   200  	if len(out) > 63 {
   201  		out = out[:63]
   202  	}
   203  
   204  	return string(out)
   205  }