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  }