From fabba5c0c8321fdd1a96ade62bfbf009c3c394e9 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Fri, 8 Sep 2017 09:25:20 +0200 Subject: [PATCH] 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 --- backend/remote-state/gcloud/backend.go | 3 +- backend/remote-state/gcloud/backend_state.go | 44 +++++++++----------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/backend/remote-state/gcloud/backend.go b/backend/remote-state/gcloud/backend.go index d06eb946a..52c826d58 100644 --- a/backend/remote-state/gcloud/backend.go +++ b/backend/remote-state/gcloud/backend.go @@ -3,6 +3,7 @@ package gcloud import ( "context" "fmt" + "strings" "cloud.google.com/go/storage" "github.com/hashicorp/terraform/backend" @@ -67,7 +68,7 @@ func (b *Backend) configure(ctx context.Context) error { data := schema.FromContextBackendConfig(b.storageContext) 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 diff --git a/backend/remote-state/gcloud/backend_state.go b/backend/remote-state/gcloud/backend_state.go index 9eb15f892..b9daf63cc 100644 --- a/backend/remote-state/gcloud/backend_state.go +++ b/backend/remote-state/gcloud/backend_state.go @@ -3,7 +3,7 @@ package gcloud import ( "errors" "fmt" - "regexp" + "path" "sort" "strings" @@ -15,42 +15,38 @@ import ( "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) { - workspaces := []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) - } - } + states := []string{backend.DefaultStateName} bucket := b.storageClient.Bucket(b.bucketName) - query := &storage.Query{ - Prefix: b.stateDir, - } - - files := bucket.Objects(b.storageContext, query) + objs := bucket.Objects(b.storageContext, &storage.Query{ + Delimiter: "/", + Prefix: b.stateDir, + }) for { - attrs, err := files.Next() + attrs, err := objs.Next() if err == iterator.Done { break } 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) - if len(matches) == 2 && matches[1] != backend.DefaultStateName { - workspaces = append(workspaces, matches[1]) + name := path.Base(attrs.Name) + if !strings.HasSuffix(name, ".tfstate") { + continue + } + st := strings.TrimSuffix(name, ".tfstate") + + if st != backend.DefaultStateName { + states = append(states, st) } } - sort.Strings(workspaces[1:]) - return workspaces, nil + sort.Strings(states[1:]) + return states, nil } func (b *Backend) DeleteState(name string) error {