Skip to content

Commit

Permalink
Merge branch 'master' into azurerm-documentdb
Browse files Browse the repository at this point in the history
  • Loading branch information
tombuildsstuff committed Mar 19, 2017
2 parents d04fdee + c101650 commit ad0b959
Show file tree
Hide file tree
Showing 398 changed files with 44,177 additions and 1,520 deletions.
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
dist: trusty
sudo: false
language: go
go:
- 1.8

env:
- CONSUL_VERSION=0.7.5 TF_CONSUL_TEST=1 GOMAXPROCS=4

# Fetch consul for the backend and provider tests
before_install:
- curl -sLo consul.zip https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip
- unzip consul.zip
- mkdir ~/bin
- mv consul ~/bin
- export PATH="~/bin:$PATH"

install:
# This script is used by the Travis build to install a cookie for
# go.googlesource.com so rate limits are higher when using `go get` to fetch
Expand Down
181 changes: 171 additions & 10 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion backend/atlas/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func (b *Backend) init() {
"address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: defaultAtlasServer,
Description: schemaDescriptions["address"],
DefaultFunc: schema.EnvDefaultFunc("ATLAS_ADDRESS", defaultAtlasServer),
},
},

Expand Down
37 changes: 37 additions & 0 deletions backend/atlas/backend_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
package atlas

import (
"os"
"testing"

"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/terraform"
)

func TestImpl(t *testing.T) {
var _ backend.Backend = new(Backend)
var _ backend.CLI = new(Backend)
}

func TestConfigure_envAddr(t *testing.T) {
defer os.Setenv("ATLAS_ADDRESS", os.Getenv("ATLAS_ADDRESS"))
os.Setenv("ATLAS_ADDRESS", "http://foo.com")

b := &Backend{}
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
"name": "foo/bar",
})))
if err != nil {
t.Fatalf("err: %s", err)
}

if b.stateClient.Server != "http://foo.com" {
t.Fatalf("bad: %#v", b.stateClient)
}
}

func TestConfigure_envToken(t *testing.T) {
defer os.Setenv("ATLAS_TOKEN", os.Getenv("ATLAS_TOKEN"))
os.Setenv("ATLAS_TOKEN", "foo")

b := &Backend{}
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
"name": "foo/bar",
})))
if err != nil {
t.Fatalf("err: %s", err)
}

if b.stateClient.AccessToken != "foo" {
t.Fatalf("bad: %#v", b.stateClient)
}
}
27 changes: 13 additions & 14 deletions backend/local/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (b *Local) States() ([]string, error) {
// the listing always start with "default"
envs := []string{backend.DefaultStateName}

entries, err := ioutil.ReadDir(DefaultEnvDir)
entries, err := ioutil.ReadDir(b.stateEnvDir())
// no error if there's no envs configured
if os.IsNotExist(err) {
return envs, nil
Expand Down Expand Up @@ -166,7 +166,7 @@ func (b *Local) DeleteState(name string) error {
}

delete(b.states, name)
return os.RemoveAll(filepath.Join(DefaultEnvDir, name))
return os.RemoveAll(filepath.Join(b.stateEnvDir(), name))
}

func (b *Local) State(name string) (state.State, error) {
Expand Down Expand Up @@ -320,17 +320,12 @@ func (b *Local) StatePaths(name string) (string, string, string) {
name = backend.DefaultStateName
}

envDir := DefaultEnvDir
if b.StateEnvDir != "" {
envDir = b.StateEnvDir
}

if name == backend.DefaultStateName {
if statePath == "" {
statePath = DefaultStateFilename
}
} else {
statePath = filepath.Join(envDir, name, DefaultStateFilename)
statePath = filepath.Join(b.stateEnvDir(), name, DefaultStateFilename)
}

if stateOutPath == "" {
Expand All @@ -353,12 +348,7 @@ func (b *Local) createState(name string) error {
return nil
}

envDir := DefaultEnvDir
if b.StateEnvDir != "" {
envDir = b.StateEnvDir
}

stateDir := filepath.Join(envDir, name)
stateDir := filepath.Join(b.stateEnvDir(), name)
s, err := os.Stat(stateDir)
if err == nil && s.IsDir() {
// no need to check for os.IsNotExist, since that is covered by os.MkdirAll
Expand All @@ -374,6 +364,15 @@ func (b *Local) createState(name string) error {
return nil
}

// stateEnvDir returns the directory where state environments are stored.
func (b *Local) stateEnvDir() string {
if b.StateEnvDir != "" {
return b.StateEnvDir
}

return DefaultEnvDir
}

// currentStateName returns the name of the current named state as set in the
// configuration files.
// If there are no configured environments, currentStateName returns "default"
Expand Down
38 changes: 23 additions & 15 deletions backend/local/backend_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"strings"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
Expand All @@ -22,24 +23,17 @@ func (b *Local) opRefresh(
if b.Backend == nil {
if _, err := os.Stat(b.StatePath); err != nil {
if os.IsNotExist(err) {
err = nil
}

if err != nil {
runningOp.Err = fmt.Errorf(
"The Terraform state file for your infrastructure does not\n"+
"exist. The 'refresh' command only works and only makes sense\n"+
"when there is existing state that Terraform is managing. Please\n"+
"double-check the value given below and try again. If you\n"+
"haven't created infrastructure with Terraform yet, use the\n"+
"'terraform apply' command.\n\n"+
"Path: %s",
b.StatePath)
"There was an error reading the Terraform state that is needed\n"+
"for refreshing. The path and error are shown below.\n\n"+
"Path: %s\n\nError: %s",
b.StatePath, err)
return
}

runningOp.Err = fmt.Errorf(
"There was an error reading the Terraform state that is needed\n"+
"for refreshing. The path and error are shown below.\n\n"+
"Path: %s\n\nError: %s",
b.StatePath, err)
return
}
}

Expand Down Expand Up @@ -74,6 +68,12 @@ func (b *Local) opRefresh(

// Set our state
runningOp.State = opState.State()
if runningOp.State.Empty() || !runningOp.State.HasResources() {
if b.CLI != nil {
b.CLI.Output(b.Colorize().Color(
strings.TrimSpace(refreshNoState) + "\n"))
}
}

// Perform operation and write the resulting state to the running op
newState, err := tfCtx.Refresh()
Expand All @@ -93,3 +93,11 @@ func (b *Local) opRefresh(
return
}
}

const refreshNoState = `
[reset][bold][yellow]Empty or non-existent state file.[reset][yellow]
Refresh will do nothing. Refresh does not error or return an erroneous
exit status because many automation scripts use refresh, plan, then apply
and may not have a state file yet for the first run.
`
5 changes: 5 additions & 0 deletions backend/local/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ func TestLocal_impl(t *testing.T) {
var _ backend.CLI = new(Local)
}

func TestLocal_backend(t *testing.T) {
b := TestLocal(t)
backend.TestBackend(t, b, b)
}

func checkState(t *testing.T, path, expected string) {
// Read the state
f, err := os.Open(path)
Expand Down
1 change: 1 addition & 0 deletions backend/local/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestLocal(t *testing.T) *Local {
StatePath: filepath.Join(tempDir, "state.tfstate"),
StateOutPath: filepath.Join(tempDir, "state.tfstate"),
StateBackupPath: filepath.Join(tempDir, "state.tfstate.bak"),
StateEnvDir: filepath.Join(tempDir, "state.tfstate.d"),
ContextOpts: &terraform.ContextOpts{},
}
}
Expand Down
19 changes: 19 additions & 0 deletions backend/remote-state/consul/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ func New() backend.Backend {
Description: "HTTP Auth in the format of 'username:password'",
Default: "", // To prevent input
},

"gzip": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Compress the state data using gzip",
Default: false,
},

"lock": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Lock state access",
Default: true,
},
},
}

Expand All @@ -64,13 +78,18 @@ func New() backend.Backend {
type Backend struct {
*schema.Backend

// The fields below are set from configure
configData *schema.ResourceData
lock bool
}

func (b *Backend) configure(ctx context.Context) error {
// Grab the resource data
b.configData = schema.FromContextBackendConfig(ctx)

// Store the lock information
b.lock = b.configData.Get("lock").(bool)

// Initialize a client to test config
_, err := b.clientRaw()
return err
Expand Down
18 changes: 15 additions & 3 deletions backend/remote-state/consul/backend_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,39 @@ func (b *Backend) State(name string) (state.State, error) {
// Determine the path of the data
path := b.path(name)

// Determine whether to gzip or not
gzip := b.configData.Get("gzip").(bool)

// Build the state client
stateMgr := &remote.State{
var stateMgr state.State = &remote.State{
Client: &RemoteClient{
Client: client,
Path: path,
GZip: gzip,
},
}

// If we're not locking, disable it
if !b.lock {
stateMgr = &state.LockDisabled{Inner: stateMgr}
}

// Get the locker, which we know always exists
stateMgrLocker := stateMgr.(state.Locker)

// Grab a lock, we use this to write an empty state if one doesn't
// exist already. We have to write an empty state as a sentinel value
// so States() knows it exists.
lockInfo := state.NewLockInfo()
lockInfo.Operation = "init"
lockId, err := stateMgr.Lock(lockInfo)
lockId, err := stateMgrLocker.Lock(lockInfo)
if err != nil {
return nil, fmt.Errorf("failed to lock state in Consul: %s", err)
}

// Local helper function so we can call it multiple places
lockUnlock := func(parent error) error {
if err := stateMgr.Unlock(lockId); err != nil {
if err := stateMgrLocker.Unlock(lockId); err != nil {
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
}

Expand Down
Loading

0 comments on commit ad0b959

Please sign in to comment.