backend/remote-state/gcloud: Refactor Backend.States().

The previous code listed all objects in the bucket and used local filtering
(using regular expressions) to find .tfstate objects. This new code sets
the delimiter to "/", which causes GCS to only return objects directly in
the given prefix, but not any sub"directories".

Fixes:
* https://github.com/golang/go/wiki/CodeReviewComments#doc-comments
* https://github.com/golang/go/wiki/CodeReviewComments#error-strings
This commit is contained in:
Florian Forster 2017-09-08 09:25:20 +02:00 committed by James Bardin
parent 97e1aa7ce9
commit fabba5c0c8
2 changed files with 22 additions and 25 deletions

View File

@ -3,6 +3,7 @@ package gcloud
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"cloud.google.com/go/storage" "cloud.google.com/go/storage"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
@ -67,7 +68,7 @@ func (b *Backend) configure(ctx context.Context) error {
data := schema.FromContextBackendConfig(b.storageContext) data := schema.FromContextBackendConfig(b.storageContext)
b.bucketName = data.Get("bucket").(string) b.bucketName = data.Get("bucket").(string)
b.stateDir = data.Get("state_dir").(string) b.stateDir = strings.TrimLeft(data.Get("state_dir").(string), "/")
var tokenSource oauth2.TokenSource var tokenSource oauth2.TokenSource

View File

@ -3,7 +3,7 @@ package gcloud
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp" "path"
"sort" "sort"
"strings" "strings"
@ -15,42 +15,38 @@ import (
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
) )
// States returns a list of names for the states found on GCS. The default
// state is always returned as the first element in the slice.
func (b *Backend) States() ([]string, error) { func (b *Backend) States() ([]string, error) {
workspaces := []string{backend.DefaultStateName} states := []string{backend.DefaultStateName}
var stateRegex *regexp.Regexp
var err error
if b.stateDir == "" {
stateRegex = regexp.MustCompile(`^(.+)\.tfstate$`)
} else {
stateRegex, err = regexp.Compile(fmt.Sprintf("^%v/(.+)\\.tfstate$", regexp.QuoteMeta(b.stateDir)))
if err != nil {
return []string{}, fmt.Errorf("Failed to compile regex for querying states: %v", err)
}
}
bucket := b.storageClient.Bucket(b.bucketName) bucket := b.storageClient.Bucket(b.bucketName)
query := &storage.Query{ objs := bucket.Objects(b.storageContext, &storage.Query{
Delimiter: "/",
Prefix: b.stateDir, Prefix: b.stateDir,
} })
files := bucket.Objects(b.storageContext, query)
for { for {
attrs, err := files.Next() attrs, err := objs.Next()
if err == iterator.Done { if err == iterator.Done {
break break
} }
if err != nil { if err != nil {
return []string{}, fmt.Errorf("Failed to query remote states: %v", err) return nil, fmt.Errorf("querying Cloud Storage failed: %v", err)
} }
matches := stateRegex.FindStringSubmatch(attrs.Name) name := path.Base(attrs.Name)
if len(matches) == 2 && matches[1] != backend.DefaultStateName { if !strings.HasSuffix(name, ".tfstate") {
workspaces = append(workspaces, matches[1]) continue
}
st := strings.TrimSuffix(name, ".tfstate")
if st != backend.DefaultStateName {
states = append(states, st)
} }
} }
sort.Strings(workspaces[1:]) sort.Strings(states[1:])
return workspaces, nil return states, nil
} }
func (b *Backend) DeleteState(name string) error { func (b *Backend) DeleteState(name string) error {