github.com/codeherentuk/terraform@v0.11.12-beta1/backend/remote-state/gcs/backend_test.go (about)

     1  package gcs
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"cloud.google.com/go/storage"
    12  	"github.com/hashicorp/terraform/backend"
    13  	"github.com/hashicorp/terraform/state/remote"
    14  )
    15  
    16  const (
    17  	noPrefix        = ""
    18  	noEncryptionKey = ""
    19  )
    20  
    21  // See https://cloud.google.com/storage/docs/using-encryption-keys#generating_your_own_encryption_key
    22  var encryptionKey = "yRyCOikXi1ZDNE0xN3yiFsJjg7LGimoLrGFcLZgQoVk="
    23  
    24  func TestStateFile(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	cases := []struct {
    28  		prefix           string
    29  		defaultStateFile string
    30  		name             string
    31  		wantStateFile    string
    32  		wantLockFile     string
    33  	}{
    34  		{"state", "", "default", "state/default.tfstate", "state/default.tflock"},
    35  		{"state", "", "test", "state/test.tfstate", "state/test.tflock"},
    36  		{"state", "legacy.tfstate", "default", "legacy.tfstate", "legacy.tflock"},
    37  		{"state", "legacy.tfstate", "test", "state/test.tfstate", "state/test.tflock"},
    38  		{"state", "legacy.state", "default", "legacy.state", "legacy.state.tflock"},
    39  		{"state", "legacy.state", "test", "state/test.tfstate", "state/test.tflock"},
    40  	}
    41  	for _, c := range cases {
    42  		b := &Backend{
    43  			prefix:           c.prefix,
    44  			defaultStateFile: c.defaultStateFile,
    45  		}
    46  
    47  		if got := b.stateFile(c.name); got != c.wantStateFile {
    48  			t.Errorf("stateFile(%q) = %q, want %q", c.name, got, c.wantStateFile)
    49  		}
    50  
    51  		if got := b.lockFile(c.name); got != c.wantLockFile {
    52  			t.Errorf("lockFile(%q) = %q, want %q", c.name, got, c.wantLockFile)
    53  		}
    54  	}
    55  }
    56  
    57  func TestRemoteClient(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	bucket := bucketName(t)
    61  	be := setupBackend(t, bucket, noPrefix, noEncryptionKey)
    62  	defer teardownBackend(t, be, noPrefix)
    63  
    64  	ss, err := be.State(backend.DefaultStateName)
    65  	if err != nil {
    66  		t.Fatalf("be.State(%q) = %v", backend.DefaultStateName, err)
    67  	}
    68  
    69  	rs, ok := ss.(*remote.State)
    70  	if !ok {
    71  		t.Fatalf("be.State(): got a %T, want a *remote.State", ss)
    72  	}
    73  
    74  	remote.TestClient(t, rs.Client)
    75  }
    76  func TestRemoteClientWithEncryption(t *testing.T) {
    77  	t.Parallel()
    78  
    79  	bucket := bucketName(t)
    80  	be := setupBackend(t, bucket, noPrefix, encryptionKey)
    81  	defer teardownBackend(t, be, noPrefix)
    82  
    83  	ss, err := be.State(backend.DefaultStateName)
    84  	if err != nil {
    85  		t.Fatalf("be.State(%q) = %v", backend.DefaultStateName, err)
    86  	}
    87  
    88  	rs, ok := ss.(*remote.State)
    89  	if !ok {
    90  		t.Fatalf("be.State(): got a %T, want a *remote.State", ss)
    91  	}
    92  
    93  	remote.TestClient(t, rs.Client)
    94  }
    95  
    96  func TestRemoteLocks(t *testing.T) {
    97  	t.Parallel()
    98  
    99  	bucket := bucketName(t)
   100  	be := setupBackend(t, bucket, noPrefix, noEncryptionKey)
   101  	defer teardownBackend(t, be, noPrefix)
   102  
   103  	remoteClient := func() (remote.Client, error) {
   104  		ss, err := be.State(backend.DefaultStateName)
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  
   109  		rs, ok := ss.(*remote.State)
   110  		if !ok {
   111  			return nil, fmt.Errorf("be.State(): got a %T, want a *remote.State", ss)
   112  		}
   113  
   114  		return rs.Client, nil
   115  	}
   116  
   117  	c0, err := remoteClient()
   118  	if err != nil {
   119  		t.Fatalf("remoteClient(0) = %v", err)
   120  	}
   121  	c1, err := remoteClient()
   122  	if err != nil {
   123  		t.Fatalf("remoteClient(1) = %v", err)
   124  	}
   125  
   126  	remote.TestRemoteLocks(t, c0, c1)
   127  }
   128  
   129  func TestBackend(t *testing.T) {
   130  	t.Parallel()
   131  
   132  	bucket := bucketName(t)
   133  
   134  	be0 := setupBackend(t, bucket, noPrefix, noEncryptionKey)
   135  	defer teardownBackend(t, be0, noPrefix)
   136  
   137  	be1 := setupBackend(t, bucket, noPrefix, noEncryptionKey)
   138  
   139  	backend.TestBackendStates(t, be0)
   140  	backend.TestBackendStateLocks(t, be0, be1)
   141  	backend.TestBackendStateForceUnlock(t, be0, be1)
   142  }
   143  
   144  func TestBackendWithPrefix(t *testing.T) {
   145  	t.Parallel()
   146  
   147  	prefix := "test/prefix"
   148  	bucket := bucketName(t)
   149  
   150  	be0 := setupBackend(t, bucket, prefix, noEncryptionKey)
   151  	defer teardownBackend(t, be0, prefix)
   152  
   153  	be1 := setupBackend(t, bucket, prefix+"/", noEncryptionKey)
   154  
   155  	backend.TestBackendStates(t, be0)
   156  	backend.TestBackendStateLocks(t, be0, be1)
   157  }
   158  func TestBackendWithEncryption(t *testing.T) {
   159  	t.Parallel()
   160  
   161  	bucket := bucketName(t)
   162  
   163  	be0 := setupBackend(t, bucket, noPrefix, encryptionKey)
   164  	defer teardownBackend(t, be0, noPrefix)
   165  
   166  	be1 := setupBackend(t, bucket, noPrefix, encryptionKey)
   167  
   168  	backend.TestBackendStates(t, be0)
   169  	backend.TestBackendStateLocks(t, be0, be1)
   170  }
   171  
   172  // setupBackend returns a new GCS backend.
   173  func setupBackend(t *testing.T, bucket, prefix, key string) backend.Backend {
   174  	t.Helper()
   175  
   176  	projectID := os.Getenv("GOOGLE_PROJECT")
   177  	if projectID == "" || os.Getenv("TF_ACC") == "" {
   178  		t.Skip("This test creates a bucket in GCS and populates it. " +
   179  			"Since this may incur costs, it will only run if " +
   180  			"the TF_ACC and GOOGLE_PROJECT environment variables are set.")
   181  	}
   182  
   183  	config := map[string]interface{}{
   184  		"project":        projectID,
   185  		"bucket":         bucket,
   186  		"prefix":         prefix,
   187  		"encryption_key": key,
   188  	}
   189  
   190  	b := backend.TestBackendConfig(t, New(), config)
   191  	be := b.(*Backend)
   192  
   193  	// create the bucket if it doesn't exist
   194  	bkt := be.storageClient.Bucket(bucket)
   195  	_, err := bkt.Attrs(be.storageContext)
   196  	if err != nil {
   197  		if err != storage.ErrBucketNotExist {
   198  			t.Fatal(err)
   199  		}
   200  
   201  		attrs := &storage.BucketAttrs{
   202  			Location: be.region,
   203  		}
   204  		err := bkt.Create(be.storageContext, be.projectID, attrs)
   205  		if err != nil {
   206  			t.Fatal(err)
   207  		}
   208  	}
   209  
   210  	return b
   211  }
   212  
   213  // teardownBackend deletes all states from be except the default state.
   214  func teardownBackend(t *testing.T, be backend.Backend, prefix string) {
   215  	t.Helper()
   216  	gcsBE, ok := be.(*Backend)
   217  	if !ok {
   218  		t.Fatalf("be is a %T, want a *gcsBackend", be)
   219  	}
   220  	ctx := gcsBE.storageContext
   221  
   222  	bucket := gcsBE.storageClient.Bucket(gcsBE.bucketName)
   223  	objs := bucket.Objects(ctx, nil)
   224  
   225  	for o, err := objs.Next(); err == nil; o, err = objs.Next() {
   226  		if err := bucket.Object(o.Name).Delete(ctx); err != nil {
   227  			log.Printf("Error trying to delete object: %s %s\n\n", o.Name, err)
   228  		} else {
   229  			log.Printf("Object deleted: %s", o.Name)
   230  		}
   231  	}
   232  
   233  	// Delete the bucket itself.
   234  	if err := bucket.Delete(ctx); err != nil {
   235  		t.Errorf("deleting bucket %q failed, manual cleanup may be required: %v", gcsBE.bucketName, err)
   236  	}
   237  }
   238  
   239  // bucketName returns a valid bucket name for this test.
   240  func bucketName(t *testing.T) string {
   241  	name := fmt.Sprintf("tf-%x-%s", time.Now().UnixNano(), t.Name())
   242  
   243  	// Bucket names must contain 3 to 63 characters.
   244  	if len(name) > 63 {
   245  		name = name[:63]
   246  	}
   247  
   248  	return strings.ToLower(name)
   249  }