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  }