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