Skip to content

Commit 7fcbfb1

Browse files
committed
Add tablestore config to store state lock
1 parent 69c87dc commit 7fcbfb1

File tree

135 files changed

+18507
-10328
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+18507
-10328
lines changed

addrs/resourcemode_string.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/local/counthookaction_string.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/operationtype_string.go

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/remote-state/oss/backend.go

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import (
1111

1212
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
1313
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
14-
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/resource"
15-
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
1614
"github.com/aliyun/alibaba-cloud-sdk-go/services/location"
15+
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
1716
"github.com/hashicorp/go-cleanhttp"
1817
"github.com/hashicorp/terraform/version"
1918
"log"
@@ -44,7 +43,7 @@ func New() backend.Backend {
4443
Type: schema.TypeString,
4544
Optional: true,
4645
Description: "Alibaba Cloud Security Token",
47-
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECURITY_TOKEN", os.Getenv("SECURITY_TOKEN")),
46+
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECURITY_TOKEN", ""),
4847
},
4948

5049
"region": &schema.Schema{
@@ -53,7 +52,12 @@ func New() backend.Backend {
5352
Description: "The region of the OSS bucket.",
5453
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", os.Getenv("ALICLOUD_DEFAULT_REGION")),
5554
},
56-
55+
"tablestore_endpoint": {
56+
Type: schema.TypeString,
57+
Optional: true,
58+
Description: "A custom endpoint for the TableStore API",
59+
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_TABLESTORE_ENDPOINT", ""),
60+
},
5761
"endpoint": {
5862
Type: schema.TypeString,
5963
Optional: true,
@@ -67,30 +71,38 @@ func New() backend.Backend {
6771
Description: "The name of the OSS bucket",
6872
},
6973

70-
"path": &schema.Schema{
74+
"prefix": &schema.Schema{
7175
Type: schema.TypeString,
72-
Required: true,
73-
Description: "The path relative to your object storage directory where the state file will be stored.",
76+
Optional: true,
77+
Description: "The directory where state files will be saved inside the bucket",
78+
Default: "env:",
79+
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
80+
prefix := v.(string)
81+
if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
82+
return nil, []error{fmt.Errorf("workspace_key_prefix must not start with '/' or './'")}
83+
}
84+
return nil, nil
85+
},
7486
},
7587

76-
"name": &schema.Schema{
88+
"key": &schema.Schema{
7789
Type: schema.TypeString,
7890
Optional: true,
79-
Description: "The name of the state file inside the bucket",
91+
Description: "The path of the state file inside the bucket",
8092
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
8193
if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
82-
return nil, []error{fmt.Errorf("name can not start and end with '/'")}
94+
return nil, []error{fmt.Errorf("key can not start and end with '/'")}
8395
}
8496
return nil, nil
8597
},
8698
Default: "terraform.tfstate",
8799
},
88100

89-
"lock": &schema.Schema{
90-
Type: schema.TypeBool,
101+
"tablestore_table": {
102+
Type: schema.TypeString,
91103
Optional: true,
92-
Description: "Whether to lock state access. Defaults to true",
93-
Default: true,
104+
Description: "TableStore table for state locking and consistency",
105+
Default: "",
94106
},
95107

96108
"encrypt": &schema.Schema{
@@ -130,14 +142,16 @@ type Backend struct {
130142

131143
// The fields below are set from configure
132144
ossClient *oss.Client
145+
otsClient *tablestore.TableStoreClient
133146

134147
bucketName string
135-
statePath string
136-
stateName string
148+
statePrefix string
149+
stateKey string
137150
serverSideEncryption bool
138151
acl string
139152
endpoint string
140-
lock bool
153+
otsEndpoint string
154+
otsTable string
141155
}
142156

143157
func (b *Backend) configure(ctx context.Context) error {
@@ -149,27 +163,20 @@ func (b *Backend) configure(ctx context.Context) error {
149163
d := schema.FromContextBackendConfig(ctx)
150164

151165
b.bucketName = d.Get("bucket").(string)
152-
dir := strings.Trim(d.Get("path").(string), "/")
153-
if strings.HasPrefix(dir, "./") {
154-
dir = strings.TrimPrefix(dir, "./")
155-
156-
}
157-
158-
b.statePath = dir
159-
b.stateName = d.Get("name").(string)
166+
b.statePrefix = strings.TrimPrefix(strings.Trim(d.Get("prefix").(string), "/"), "./")
167+
b.stateKey = d.Get("key").(string)
160168
b.serverSideEncryption = d.Get("encrypt").(bool)
161169
b.acl = d.Get("acl").(string)
162-
b.lock = d.Get("lock").(bool)
163170

164-
access_key := d.Get("access_key").(string)
165-
secret_key := d.Get("secret_key").(string)
166-
security_token := d.Get("security_token").(string)
171+
accessKey := d.Get("access_key").(string)
172+
secretKey := d.Get("secret_key").(string)
173+
securityToken := d.Get("security_token").(string)
167174
region := d.Get("region").(string)
168175
endpoint := d.Get("endpoint").(string)
169176
schma := "https"
170177

171178
if endpoint == "" {
172-
endpointItem, _ := b.getOSSEndpointByRegion(access_key, secret_key, security_token, region)
179+
endpointItem, _ := b.getOSSEndpointByRegion(accessKey, secretKey, securityToken, region)
173180
if endpointItem != nil && len(endpointItem.Endpoint) > 0 {
174181
if len(endpointItem.Protocols.Protocols) > 0 {
175182
// HTTP or HTTPS
@@ -191,13 +198,23 @@ func (b *Backend) configure(ctx context.Context) error {
191198
}
192199
log.Printf("[DEBUG] Instantiate OSS client using endpoint: %#v", endpoint)
193200
var options []oss.ClientOption
194-
if security_token != "" {
195-
options = append(options, oss.SecurityToken(security_token))
201+
if securityToken != "" {
202+
options = append(options, oss.SecurityToken(securityToken))
196203
}
197204
options = append(options, oss.UserAgent(fmt.Sprintf("%s/%s", TerraformUA, TerraformVersion)))
198205

199-
client, err := oss.New(endpoint, access_key, secret_key, options...)
206+
client, err := oss.New(endpoint, accessKey, secretKey, options...)
200207
b.ossClient = client
208+
otsEndpoint := d.Get("tablestore_endpoint").(string)
209+
if otsEndpoint != "" {
210+
if !strings.HasPrefix(otsEndpoint, "http") {
211+
otsEndpoint = fmt.Sprintf("%s://%s", schma, otsEndpoint)
212+
}
213+
b.otsEndpoint = otsEndpoint
214+
parts := strings.Split(strings.TrimPrefix(strings.TrimPrefix(otsEndpoint, "https://"), "http://"), ".")
215+
b.otsClient = tablestore.NewClientWithConfig(otsEndpoint, parts[0], accessKey, secretKey, securityToken, tablestore.NewDefaultTableStoreConfig())
216+
}
217+
b.otsTable = d.Get("tablestore_table").(string)
201218

202219
return err
203220
}
@@ -222,11 +239,6 @@ func (b *Backend) getOSSEndpointByRegion(access_key, secret_key, security_token,
222239
}
223240

224241
func getSdkConfig() *sdk.Config {
225-
// Fix bug "open /usr/local/go/lib/time/zoneinfo.zip: no such file or directory" which happened in windows.
226-
if data, ok := resource.GetTZData("GMT"); ok {
227-
utils.TZData = data
228-
utils.LoadLocationFromTZData = time.LoadLocationFromTZData
229-
}
230242
return sdk.NewConfig().
231243
WithMaxRetryTime(5).
232244
WithTimeout(time.Duration(30) * time.Second).

backend/remote-state/oss/backend_state.go

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/hashicorp/terraform/state/remote"
1313
"github.com/hashicorp/terraform/states"
1414

15+
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
1516
"log"
1617
"path"
1718
)
@@ -33,7 +34,32 @@ func (b *Backend) remoteClient(name string) (*RemoteClient, error) {
3334
lockFile: b.lockFile(name),
3435
serverSideEncryption: b.serverSideEncryption,
3536
acl: b.acl,
36-
doLock: b.lock,
37+
otsTable: b.otsTable,
38+
otsClient: b.otsClient,
39+
}
40+
if b.otsEndpoint != "" && b.otsTable != "" {
41+
table, err := b.otsClient.DescribeTable(&tablestore.DescribeTableRequest{
42+
TableName: b.otsTable,
43+
})
44+
if err != nil {
45+
return client, fmt.Errorf("Error describing table store %s: %#v", b.otsTable, err)
46+
}
47+
for _, t := range table.TableMeta.SchemaEntry {
48+
pkMeta := TableStorePrimaryKeyMeta{
49+
PKName: *t.Name,
50+
}
51+
if *t.Type == tablestore.PrimaryKeyType_INTEGER {
52+
pkMeta.PKType = "Integer"
53+
} else if *t.Type == tablestore.PrimaryKeyType_STRING {
54+
pkMeta.PKType = "String"
55+
} else if *t.Type == tablestore.PrimaryKeyType_BINARY {
56+
pkMeta.PKType = "Binary"
57+
} else {
58+
return client, fmt.Errorf("Unsupported PrimaryKey type: %d.", *t.Type)
59+
}
60+
client.otsTabkePK = pkMeta
61+
break
62+
}
3763
}
3864

3965
return client, nil
@@ -46,17 +72,25 @@ func (b *Backend) Workspaces() ([]string, error) {
4672
}
4773

4874
var options []oss.Option
49-
options = append(options, oss.Prefix(b.statePath))
75+
options = append(options, oss.Prefix(b.statePrefix+"/"))
5076
resp, err := bucket.ListObjects(options...)
5177

5278
if err != nil {
5379
return nil, err
5480
}
5581

5682
result := []string{backend.DefaultStateName}
83+
prefix := b.statePrefix
5784
for _, obj := range resp.Objects {
58-
if b.keyEnv(obj.Key) != "" {
59-
result = append(result, b.keyEnv(obj.Key))
85+
// we have 3 parts, the state prefix, the workspace name, and the state file: <prefix>/<worksapce-name>/<key>
86+
if path.Join(b.statePrefix, b.stateKey) == obj.Key {
87+
// filter the default workspace
88+
continue
89+
}
90+
91+
parts := strings.Split(strings.TrimPrefix(obj.Key, prefix+"/"), "/")
92+
if len(parts) > 0 && parts[0] != "" {
93+
result = append(result, parts[0])
6094
}
6195
}
6296

@@ -83,16 +117,13 @@ func (b *Backend) StateMgr(name string) (state.State, error) {
83117
}
84118
stateMgr := &remote.State{Client: client}
85119

86-
if !b.lock {
87-
stateMgr.DisableLocks()
88-
}
89120
// Check to see if this state already exists.
90121
existing, err := b.Workspaces()
91122
if err != nil {
92123
return nil, err
93124
}
94125

95-
log.Printf("[DEBUG] Current state name: %s. All States:%#v", name, existing)
126+
log.Printf("[DEBUG] Current workspace name: %s. All workspaces:%#v", name, existing)
96127

97128
exists := false
98129
for _, s := range existing {
@@ -146,41 +177,15 @@ func (b *Backend) StateMgr(name string) (state.State, error) {
146177
return stateMgr, nil
147178
}
148179

149-
// extract the object name from the OSS key
150-
func (b *Backend) keyEnv(key string) string {
151-
// we have 3 parts, the state path, the state name, and the state file
152-
parts := strings.Split(key, "/")
153-
length := len(parts)
154-
if length < 3 {
155-
// use default state
156-
return ""
157-
}
158-
159-
// shouldn't happen since we listed by prefix
160-
if strings.Join(parts[0:length-2], "/") != b.statePath {
161-
return ""
162-
}
163-
164-
// not our key, so don't include it in our listing
165-
if parts[length-1] != b.stateName {
166-
return ""
167-
}
168-
169-
return parts[length-2]
170-
}
171-
172180
func (b *Backend) stateFile(name string) string {
173181
if name == backend.DefaultStateName {
174-
return path.Join(b.statePath, b.stateName)
182+
return path.Join(b.statePrefix, b.stateKey)
175183
}
176-
return path.Join(b.statePath, name, b.stateName)
184+
return path.Join(b.statePrefix, name, b.stateKey)
177185
}
178186

179187
func (b *Backend) lockFile(name string) string {
180-
if name == backend.DefaultStateName {
181-
return path.Join(b.statePath, b.stateName+lockFileSuffix)
182-
}
183-
return path.Join(b.statePath, name, b.stateName+lockFileSuffix)
188+
return b.stateFile(name) + lockFileSuffix
184189
}
185190

186191
const stateUnlockError = `

0 commit comments

Comments
 (0)