github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/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.StateMgr(backend.DefaultStateName) 65 if err != nil { 66 t.Fatalf("be.StateMgr(%q) = %v", backend.DefaultStateName, err) 67 } 68 69 rs, ok := ss.(*remote.State) 70 if !ok { 71 t.Fatalf("be.StateMgr(): 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.StateMgr(backend.DefaultStateName) 84 if err != nil { 85 t.Fatalf("be.StateMgr(%q) = %v", backend.DefaultStateName, err) 86 } 87 88 rs, ok := ss.(*remote.State) 89 if !ok { 90 t.Fatalf("be.StateMgr(): 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.StateMgr(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.StateMgr(): 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 "bucket": bucket, 185 "prefix": prefix, 186 "encryption_key": key, 187 } 188 189 b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)) 190 be := b.(*Backend) 191 192 // create the bucket if it doesn't exist 193 bkt := be.storageClient.Bucket(bucket) 194 _, err := bkt.Attrs(be.storageContext) 195 if err != nil { 196 if err != storage.ErrBucketNotExist { 197 t.Fatal(err) 198 } 199 200 attrs := &storage.BucketAttrs{ 201 Location: os.Getenv("GOOGLE_REGION"), 202 } 203 err := bkt.Create(be.storageContext, projectID, attrs) 204 if err != nil { 205 t.Fatal(err) 206 } 207 } 208 209 return b 210 } 211 212 // teardownBackend deletes all states from be except the default state. 213 func teardownBackend(t *testing.T, be backend.Backend, prefix string) { 214 t.Helper() 215 gcsBE, ok := be.(*Backend) 216 if !ok { 217 t.Fatalf("be is a %T, want a *gcsBackend", be) 218 } 219 ctx := gcsBE.storageContext 220 221 bucket := gcsBE.storageClient.Bucket(gcsBE.bucketName) 222 objs := bucket.Objects(ctx, nil) 223 224 for o, err := objs.Next(); err == nil; o, err = objs.Next() { 225 if err := bucket.Object(o.Name).Delete(ctx); err != nil { 226 log.Printf("Error trying to delete object: %s %s\n\n", o.Name, err) 227 } else { 228 log.Printf("Object deleted: %s", o.Name) 229 } 230 } 231 232 // Delete the bucket itself. 233 if err := bucket.Delete(ctx); err != nil { 234 t.Errorf("deleting bucket %q failed, manual cleanup may be required: %v", gcsBE.bucketName, err) 235 } 236 } 237 238 // bucketName returns a valid bucket name for this test. 239 func bucketName(t *testing.T) string { 240 name := fmt.Sprintf("tf-%x-%s", time.Now().UnixNano(), t.Name()) 241 242 // Bucket names must contain 3 to 63 characters. 243 if len(name) > 63 { 244 name = name[:63] 245 } 246 247 return strings.ToLower(name) 248 }