github.com/kevinklinger/open_terraform@v1.3.6/noninternal/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/kevinklinger/open_terraform/noninternal/backend" 12 "github.com/kevinklinger/open_terraform/noninternal/legacy/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 "accelerate": { 117 Type: schema.TypeBool, 118 Optional: true, 119 Description: "Whether to enable global Acceleration", 120 Default: false, 121 }, 122 }, 123 } 124 125 result := &Backend{Backend: s} 126 result.Backend.ConfigureFunc = result.configure 127 128 return result 129 } 130 131 // configure init cos client 132 func (b *Backend) configure(ctx context.Context) error { 133 if b.cosClient != nil { 134 return nil 135 } 136 137 b.cosContext = ctx 138 data := schema.FromContextBackendConfig(b.cosContext) 139 140 b.region = data.Get("region").(string) 141 b.bucket = data.Get("bucket").(string) 142 b.prefix = data.Get("prefix").(string) 143 b.key = data.Get("key").(string) 144 b.encrypt = data.Get("encrypt").(bool) 145 b.acl = data.Get("acl").(string) 146 147 var ( 148 u *url.URL 149 err error 150 ) 151 accelerate := data.Get("accelerate").(bool) 152 if accelerate { 153 u, err = url.Parse(fmt.Sprintf("https://%s.cos.accelerate.myqcloud.com", b.bucket)) 154 } else { 155 u, err = url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", b.bucket, b.region)) 156 } 157 if err != nil { 158 return err 159 } 160 161 b.cosClient = cos.NewClient( 162 &cos.BaseURL{BucketURL: u}, 163 &http.Client{ 164 Timeout: 60 * time.Second, 165 Transport: &cos.AuthorizationTransport{ 166 SecretID: data.Get("secret_id").(string), 167 SecretKey: data.Get("secret_key").(string), 168 }, 169 }, 170 ) 171 172 credential := common.NewCredential( 173 data.Get("secret_id").(string), 174 data.Get("secret_key").(string), 175 ) 176 177 cpf := profile.NewClientProfile() 178 cpf.HttpProfile.ReqMethod = "POST" 179 cpf.HttpProfile.ReqTimeout = 300 180 cpf.Language = "en-US" 181 b.tagClient, err = tag.NewClient(credential, b.region, cpf) 182 183 return err 184 }