Merge pull request #20432 from hashicorp/jbardin/s3-prefix-key

s3 workspace_key_prefix
This commit is contained in:
James Bardin 2019-02-22 21:00:44 -05:00 committed by GitHub
commit 9c0e3cc819
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 21 deletions

View File

@ -2,7 +2,7 @@ package s3
import ( import (
"context" "context"
"fmt" "errors"
"strings" "strings"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
@ -31,7 +31,7 @@ func New() backend.Backend {
// s3 will strip leading slashes from an object, so while this will // s3 will strip leading slashes from an object, so while this will
// technically be accepted by s3, it will break our workspace hierarchy. // technically be accepted by s3, it will break our workspace hierarchy.
if strings.HasPrefix(v.(string), "/") { if strings.HasPrefix(v.(string), "/") {
return nil, []error{fmt.Errorf("key must not start with '/'")} return nil, []error{errors.New("key must not start with '/'")}
} }
return nil, nil return nil, nil
}, },
@ -211,8 +211,15 @@ func New() backend.Backend {
"workspace_key_prefix": { "workspace_key_prefix": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "The prefix applied to the non-default state path inside the bucket", Description: "The prefix applied to the non-default state path inside the bucket.",
Default: "env:", Default: "env:",
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
prefix := v.(string)
if strings.HasPrefix(prefix, "/") || strings.HasSuffix(prefix, "/") {
return nil, []error{errors.New("workspace_key_prefix must not start or end with '/'")}
}
return nil, nil
},
}, },
"force_path_style": { "force_path_style": {

View File

@ -3,6 +3,7 @@ package s3
import ( import (
"errors" "errors"
"fmt" "fmt"
"path"
"sort" "sort"
"strings" "strings"
@ -17,12 +18,12 @@ import (
) )
func (b *Backend) Workspaces() ([]string, error) { func (b *Backend) Workspaces() ([]string, error) {
prefix := b.workspaceKeyPrefix + "/" prefix := ""
// List bucket root if there is no workspaceKeyPrefix if b.workspaceKeyPrefix != "" {
if b.workspaceKeyPrefix == "" { prefix = b.workspaceKeyPrefix + "/"
prefix = ""
} }
params := &s3.ListObjectsInput{ params := &s3.ListObjectsInput{
Bucket: &b.bucketName, Bucket: &b.bucketName,
Prefix: aws.String(prefix), Prefix: aws.String(prefix),
@ -49,7 +50,9 @@ func (b *Backend) Workspaces() ([]string, error) {
} }
func (b *Backend) keyEnv(key string) string { func (b *Backend) keyEnv(key string) string {
if b.workspaceKeyPrefix == "" { prefix := b.workspaceKeyPrefix
if prefix == "" {
parts := strings.SplitN(key, "/", 2) parts := strings.SplitN(key, "/", 2)
if len(parts) > 1 && parts[1] == b.keyName { if len(parts) > 1 && parts[1] == b.keyName {
return parts[0] return parts[0]
@ -58,29 +61,31 @@ func (b *Backend) keyEnv(key string) string {
} }
} }
parts := strings.SplitAfterN(key, b.workspaceKeyPrefix, 2) // add a slash to treat this as a directory
prefix += "/"
parts := strings.SplitAfterN(key, prefix, 2)
if len(parts) < 2 { if len(parts) < 2 {
return "" return ""
} }
// shouldn't happen since we listed by prefix // shouldn't happen since we listed by prefix
if parts[0] != b.workspaceKeyPrefix { if parts[0] != prefix {
return "" return ""
} }
parts = strings.SplitN(parts[1], "/", 3) parts = strings.SplitN(parts[1], "/", 2)
if len(parts) < 3 { if len(parts) < 2 {
return "" return ""
} }
// not our key, so don't include it in our listing // not our key, so don't include it in our listing
if parts[2] != b.keyName { if parts[1] != b.keyName {
return "" return ""
} }
return parts[1] return parts[0]
} }
func (b *Backend) DeleteWorkspace(name string) error { func (b *Backend) DeleteWorkspace(name string) error {
@ -201,12 +206,7 @@ func (b *Backend) path(name string) string {
return b.keyName return b.keyName
} }
if b.workspaceKeyPrefix != "" { return path.Join(b.workspaceKeyPrefix, name, b.keyName)
return strings.Join([]string{b.workspaceKeyPrefix, name, b.keyName}, "/")
} else {
// Trim the leading / for no workspace prefix
return strings.Join([]string{b.workspaceKeyPrefix, name, b.keyName}, "/")[1:]
}
} }
const errStateUnlock = ` const errStateUnlock = `

View File

@ -199,6 +199,11 @@ func TestBackendExtraPaths(t *testing.T) {
} }
// remove the state with extra subkey // remove the state with extra subkey
if err := client.Delete(); err != nil {
t.Fatal(err)
}
// delete the real workspace
if err := b.DeleteWorkspace("s2"); err != nil { if err := b.DeleteWorkspace("s2"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -216,7 +221,7 @@ func TestBackendExtraPaths(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if stateMgr.StateSnapshotMeta().Lineage == s2Lineage { if s2Mgr.(*remote.State).StateSnapshotMeta().Lineage == s2Lineage {
t.Fatal("state s2 was not deleted") t.Fatal("state s2 was not deleted")
} }
s2 = s2Mgr.State() s2 = s2Mgr.State()