github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/remote-state/cos/backend.go (about) 1 package cos 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" 14 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" 15 tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" 16 "github.com/tencentyun/cos-go-sdk-v5" 17 ) 18 19 // Default value from environment variable 20 const ( 21 PROVIDER_SECRET_ID = "TENCENTCLOUD_SECRET_ID" 22 PROVIDER_SECRET_KEY = "TENCENTCLOUD_SECRET_KEY" 23 PROVIDER_REGION = "TENCENTCLOUD_REGION" 24 ) 25 26 // Backend implements "backend".Backend for tencentCloud cos 27 type Backend struct { 28 *schema.Backend 29 30 cosContext context.Context 31 cosClient *cos.Client 32 tagClient *tag.Client 33 34 region string 35 bucket string 36 prefix string 37 key string 38 encrypt bool 39 acl string 40 } 41 42 // New creates a new backend for TencentCloud cos remote state. 43 func New() backend.Backend { 44 s := &schema.Backend{ 45 Schema: map[string]*schema.Schema{ 46 "secret_id": { 47 Type: schema.TypeString, 48 Required: true, 49 DefaultFunc: schema.EnvDefaultFunc(PROVIDER_SECRET_ID, nil), 50 Description: "Secret id of Tencent Cloud", 51 }, 52 "secret_key": { 53 Type: schema.TypeString, 54 Required: true, 55 DefaultFunc: schema.EnvDefaultFunc(PROVIDER_SECRET_KEY, nil), 56 Description: "Secret key of Tencent Cloud", 57 Sensitive: true, 58 }, 59 "region": { 60 Type: schema.TypeString, 61 Required: true, 62 DefaultFunc: schema.EnvDefaultFunc(PROVIDER_REGION, nil), 63 Description: "The region of the COS bucket", 64 InputDefault: "ap-guangzhou", 65 }, 66 "bucket": { 67 Type: schema.TypeString, 68 Required: true, 69 Description: "The name of the COS bucket", 70 }, 71 "prefix": { 72 Type: schema.TypeString, 73 Optional: true, 74 Description: "The directory for saving the state file in bucket", 75 ValidateFunc: func(v interface{}, s string) ([]string, []error) { 76 prefix := v.(string) 77 if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") { 78 return nil, []error{fmt.Errorf("prefix must not start with '/' or './'")} 79 } 80 return nil, nil 81 }, 82 }, 83 "key": { 84 Type: schema.TypeString, 85 Optional: true, 86 Description: "The path for saving the state file in bucket", 87 Default: "terraform.tfstate", 88 ValidateFunc: func(v interface{}, s string) ([]string, []error) { 89 if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") { 90 return nil, []error{fmt.Errorf("key can not start and end with '/'")} 91 } 92 return nil, nil 93 }, 94 }, 95 "encrypt": { 96 Type: schema.TypeBool, 97 Optional: true, 98 Description: "Whether to enable server side encryption of the state file", 99 Default: true, 100 }, 101 "acl": { 102 Type: schema.TypeString, 103 Optional: true, 104 Description: "Object ACL to be applied to the state file", 105 Default: "private", 106 ValidateFunc: func(v interface{}, s string) ([]string, []error) { 107 value := v.(string) 108 if value != "private" && value != "public-read" { 109 return nil, []error{fmt.Errorf( 110 "acl value invalid, expected %s or %s, got %s", 111 "private", "public-read", value)} 112 } 113 return nil, nil 114 }, 115 }, 116 }, 117 } 118 119 result := &Backend{Backend: s} 120 result.Backend.ConfigureFunc = result.configure 121 122 return result 123 } 124 125 // configure init cos client 126 func (b *Backend) configure(ctx context.Context) error { 127 if b.cosClient != nil { 128 return nil 129 } 130 131 b.cosContext = ctx 132 data := schema.FromContextBackendConfig(b.cosContext) 133 134 b.region = data.Get("region").(string) 135 b.bucket = data.Get("bucket").(string) 136 b.prefix = data.Get("prefix").(string) 137 b.key = data.Get("key").(string) 138 b.encrypt = data.Get("encrypt").(bool) 139 b.acl = data.Get("acl").(string) 140 141 u, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", b.bucket, b.region)) 142 if err != nil { 143 return err 144 } 145 146 b.cosClient = cos.NewClient( 147 &cos.BaseURL{BucketURL: u}, 148 &http.Client{ 149 Timeout: 60 * time.Second, 150 Transport: &cos.AuthorizationTransport{ 151 SecretID: data.Get("secret_id").(string), 152 SecretKey: data.Get("secret_key").(string), 153 }, 154 }, 155 ) 156 157 credential := common.NewCredential( 158 data.Get("secret_id").(string), 159 data.Get("secret_key").(string), 160 ) 161 162 cpf := profile.NewClientProfile() 163 cpf.HttpProfile.ReqMethod = "POST" 164 cpf.HttpProfile.ReqTimeout = 300 165 cpf.Language = "en-US" 166 b.tagClient, err = tag.NewClient(credential, b.region, cpf) 167 168 return err 169 }