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