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 }