github.com/opentofu/opentofu@v1.7.1/internal/backend/remote-state/oss/backend_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package oss 7 8 import ( 9 "fmt" 10 "math/rand" 11 "os" 12 "testing" 13 "time" 14 15 "strings" 16 17 "github.com/aliyun/aliyun-oss-go-sdk/oss" 18 "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" 19 "github.com/opentofu/opentofu/internal/backend" 20 "github.com/opentofu/opentofu/internal/configs/hcl2shim" 21 "github.com/opentofu/opentofu/internal/encryption" 22 ) 23 24 // verify that we are doing ACC tests or the OSS tests specifically 25 func testACC(t *testing.T) { 26 t.Helper() 27 skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_OSS_TEST") == "" 28 if skip { 29 t.Log("oss backend tests require setting TF_ACC or TF_OSS_TEST") 30 t.Skip() 31 } 32 if skip { 33 t.Fatal("oss backend tests require setting ALICLOUD_ACCESS_KEY or ALICLOUD_ACCESS_KEY_ID") 34 } 35 t.Setenv("ALICLOUD_REGION", "cn-beijing") 36 } 37 38 func TestBackend_impl(t *testing.T) { 39 var _ backend.Backend = new(Backend) 40 } 41 42 func TestBackendConfig(t *testing.T) { 43 testACC(t) 44 config := map[string]interface{}{ 45 "region": "cn-beijing", 46 "bucket": "terraform-backend-oss-test", 47 "prefix": "mystate", 48 "key": "first.tfstate", 49 "tablestore_endpoint": "https://terraformstate.cn-beijing.ots.aliyuncs.com", 50 "tablestore_table": "TableStore", 51 } 52 53 b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend) 54 55 if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") { 56 t.Fatalf("Incorrect region was provided") 57 } 58 if b.bucketName != "terraform-backend-oss-test" { 59 t.Fatalf("Incorrect bucketName was provided") 60 } 61 if b.statePrefix != "mystate" { 62 t.Fatalf("Incorrect state file path was provided") 63 } 64 if b.stateKey != "first.tfstate" { 65 t.Fatalf("Incorrect keyName was provided") 66 } 67 68 if b.ossClient.Config.AccessKeyID == "" { 69 t.Fatalf("No Access Key Id was provided") 70 } 71 if b.ossClient.Config.AccessKeySecret == "" { 72 t.Fatalf("No Secret Access Key was provided") 73 } 74 } 75 76 func TestBackendConfigWorkSpace(t *testing.T) { 77 testACC(t) 78 bucketName := fmt.Sprintf("terraform-backend-oss-test-%d", rand.Intn(1000)) 79 config := map[string]interface{}{ 80 "region": "cn-beijing", 81 "bucket": bucketName, 82 "prefix": "mystate", 83 "key": "first.tfstate", 84 "tablestore_endpoint": "https://terraformstate.cn-beijing.ots.aliyuncs.com", 85 "tablestore_table": "TableStore", 86 } 87 88 b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend) 89 createOSSBucket(t, b.ossClient, bucketName) 90 defer deleteOSSBucket(t, b.ossClient, bucketName) 91 if _, err := b.Workspaces(); err != nil { 92 t.Fatal(err.Error()) 93 } 94 if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") { 95 t.Fatalf("Incorrect region was provided") 96 } 97 if b.bucketName != bucketName { 98 t.Fatalf("Incorrect bucketName was provided") 99 } 100 if b.statePrefix != "mystate" { 101 t.Fatalf("Incorrect state file path was provided") 102 } 103 if b.stateKey != "first.tfstate" { 104 t.Fatalf("Incorrect keyName was provided") 105 } 106 107 if b.ossClient.Config.AccessKeyID == "" { 108 t.Fatalf("No Access Key Id was provided") 109 } 110 if b.ossClient.Config.AccessKeySecret == "" { 111 t.Fatalf("No Secret Access Key was provided") 112 } 113 } 114 115 func TestBackendConfigProfile(t *testing.T) { 116 testACC(t) 117 config := map[string]interface{}{ 118 "region": "cn-beijing", 119 "bucket": "terraform-backend-oss-test", 120 "prefix": "mystate", 121 "key": "first.tfstate", 122 "tablestore_endpoint": "https://terraformstate.cn-beijing.ots.aliyuncs.com", 123 "tablestore_table": "TableStore", 124 "profile": "default", 125 } 126 127 b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend) 128 129 if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") { 130 t.Fatalf("Incorrect region was provided") 131 } 132 if b.bucketName != "terraform-backend-oss-test" { 133 t.Fatalf("Incorrect bucketName was provided") 134 } 135 if b.statePrefix != "mystate" { 136 t.Fatalf("Incorrect state file path was provided") 137 } 138 if b.stateKey != "first.tfstate" { 139 t.Fatalf("Incorrect keyName was provided") 140 } 141 142 if b.ossClient.Config.AccessKeyID == "" { 143 t.Fatalf("No Access Key Id was provided") 144 } 145 if b.ossClient.Config.AccessKeySecret == "" { 146 t.Fatalf("No Secret Access Key was provided") 147 } 148 } 149 150 func TestBackendConfig_invalidKey(t *testing.T) { 151 testACC(t) 152 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{ 153 "region": "cn-beijing", 154 "bucket": "terraform-backend-oss-test", 155 "prefix": "/leading-slash", 156 "name": "/test.tfstate", 157 "tablestore_endpoint": "https://terraformstate.cn-beijing.ots.aliyuncs.com", 158 "tablestore_table": "TableStore", 159 }) 160 161 _, results := New(encryption.StateEncryptionDisabled()).PrepareConfig(cfg) 162 if !results.HasErrors() { 163 t.Fatal("expected config validation error") 164 } 165 } 166 167 func TestBackend(t *testing.T) { 168 testACC(t) 169 170 bucketName := fmt.Sprintf("terraform-remote-oss-test-%x", time.Now().Unix()) 171 statePrefix := "multi/level/path/" 172 173 b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{ 174 "bucket": bucketName, 175 "prefix": statePrefix, 176 })).(*Backend) 177 178 b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{ 179 "bucket": bucketName, 180 "prefix": statePrefix, 181 })).(*Backend) 182 183 createOSSBucket(t, b1.ossClient, bucketName) 184 defer deleteOSSBucket(t, b1.ossClient, bucketName) 185 186 backend.TestBackendStates(t, b1) 187 backend.TestBackendStateLocks(t, b1, b2) 188 backend.TestBackendStateForceUnlock(t, b1, b2) 189 } 190 191 func createOSSBucket(t *testing.T, ossClient *oss.Client, bucketName string) { 192 // Be clear about what we're doing in case the user needs to clean this up later. 193 if err := ossClient.CreateBucket(bucketName); err != nil { 194 t.Fatal("failed to create test OSS bucket:", err) 195 } 196 } 197 198 func deleteOSSBucket(t *testing.T, ossClient *oss.Client, bucketName string) { 199 warning := "WARNING: Failed to delete the test OSS bucket. It may have been left in your Alibaba Cloud account and may incur storage charges. (error was %s)" 200 201 // first we have to get rid of the env objects, or we can't delete the bucket 202 bucket, err := ossClient.Bucket(bucketName) 203 if err != nil { 204 t.Fatal("Error getting bucket:", err) 205 return 206 } 207 objects, err := bucket.ListObjects() 208 if err != nil { 209 t.Logf(warning, err) 210 return 211 } 212 for _, obj := range objects.Objects { 213 if err := bucket.DeleteObject(obj.Key); err != nil { 214 // this will need cleanup no matter what, so just warn and exit 215 t.Logf(warning, err) 216 return 217 } 218 } 219 220 if err := ossClient.DeleteBucket(bucketName); err != nil { 221 t.Logf(warning, err) 222 } 223 } 224 225 // create the tablestore table, and wait until we can query it. 226 func createTablestoreTable(t *testing.T, otsClient *tablestore.TableStoreClient, tableName string) { 227 tableMeta := new(tablestore.TableMeta) 228 tableMeta.TableName = tableName 229 tableMeta.AddPrimaryKeyColumn(pkName, tablestore.PrimaryKeyType_STRING) 230 231 tableOption := new(tablestore.TableOption) 232 tableOption.TimeToAlive = -1 233 tableOption.MaxVersion = 1 234 235 reservedThroughput := new(tablestore.ReservedThroughput) 236 237 _, err := otsClient.CreateTable(&tablestore.CreateTableRequest{ 238 TableMeta: tableMeta, 239 TableOption: tableOption, 240 ReservedThroughput: reservedThroughput, 241 }) 242 if err != nil { 243 t.Fatal(err) 244 } 245 } 246 247 func deleteTablestoreTable(t *testing.T, otsClient *tablestore.TableStoreClient, tableName string) { 248 params := &tablestore.DeleteTableRequest{ 249 TableName: tableName, 250 } 251 _, err := otsClient.DeleteTable(params) 252 if err != nil { 253 t.Logf("WARNING: Failed to delete the test TableStore table %q. It has been left in your Alibaba Cloud account and may incur charges. (error was %s)", tableName, err) 254 } 255 }