Skip to content

Commit

Permalink
Merge branch 'master' into f-helperschema-timeout
Browse files Browse the repository at this point in the history
* master: (499 commits)
  provider/aws: Fix the panic in ssm_association with parameters (#12215)
  Setting incident_urgency_rule as optional (#12211)
  Latest Section (#12151)
  provider/pagerduty: Import support for service integrations (#12141)
  command: fix test for new Meta type
  terraform: InstanceState.Meta is value type interface{}
  Update CHANGELOG.md
  flatmap: mark computed list as a computed value in Expand
  provider/azurerm: Bump AzureRM SDK to v8.0.1-beta (#11866)
  provider/profitbricks: fmt changes post cherry-pick
  provider/aws: Add missing SSM Links to documentation nav bar (#12202)
  update azure_network_interface documentation to say required for ip_configuration (#12185)
  Profitbricks primary nic fix (#12197)
  Update CHANGELOG.md
  Update CHANGELOG.md
  Update CHANGELOG.md
  Update CHANGELOG.md
  provider/aws: Lambda DeadLetterConfig support
  what was the value of the cluster id which caused the problem
  Corrected example code, cleared up token confusion (#12177)
  ...
  • Loading branch information
catsby committed Feb 23, 2017
2 parents 8d14c4e + 9fc5775 commit 829a8a7
Show file tree
Hide file tree
Showing 1,614 changed files with 126,204 additions and 20,604 deletions.
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

*.go eol=lf
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
sudo: false
language: go
go:
- 1.7.5
- 1.8rc3
- 1.8
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
219 changes: 211 additions & 8 deletions CHANGELOG.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,9 @@ errcheck:
vendor-status:
@govendor status

# disallow any parallelism (-j) for Make. This is necessary since some
# commands during the build process create temporary files that collide
# under parallel conditions.
.NOTPARALLEL:

.PHONY: bin core-dev core-test cover default dev errcheck fmt fmtcheck generate plugin-dev quickdev test-compile test testacc testrace tools vendor-status vet
2 changes: 1 addition & 1 deletion Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
VAGRANTFILE_API_VERSION = "2"

# Software version variables
GOVERSION = "1.7.5"
GOVERSION = "1.8"
UBUNTUVERSION = "16.04"

# CPU and RAM can be adjusted depending on your system
Expand Down
10 changes: 9 additions & 1 deletion backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ type Backend interface {

// State returns the current state for this environment. This state may
// not be loaded locally: the proper APIs should be called on state.State
// to load the state.
// to load the state. If the state.State is a state.Locker, it's up to the
// caller to call Lock and Unlock as needed.
State() (state.State, error)
}

Expand All @@ -38,6 +39,9 @@ type Enhanced interface {
// It is up to the implementation to determine what "performing" means.
// This DOES NOT BLOCK. The context returned as part of RunningOperation
// should be used to block for completion.
// If the state used in the operation can be locked, it is the
// responsibility of the Backend to lock the state for the duration of the
// running operation.
Operation(context.Context, *Operation) (*RunningOperation, error)
}

Expand Down Expand Up @@ -99,6 +103,10 @@ type Operation struct {
// Input/output/control options.
UIIn terraform.UIInput
UIOut terraform.UIOutput

// If LockState is true, the Operation must Lock any
// state.Lockers for its duration, and Unlock when complete.
LockState bool
}

// RunningOperation is the result of starting an operation.
Expand Down
69 changes: 69 additions & 0 deletions backend/init/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Package init contains the list of backends that can be initialized and
// basic helper functions for initializing those backends.
package init

import (
"sync"

"github.com/hashicorp/terraform/backend"

backendlegacy "github.com/hashicorp/terraform/backend/legacy"
backendlocal "github.com/hashicorp/terraform/backend/local"
backendconsul "github.com/hashicorp/terraform/backend/remote-state/consul"
backendinmem "github.com/hashicorp/terraform/backend/remote-state/inmem"
)

// backends is the list of available backends. This is a global variable
// because backends are currently hardcoded into Terraform and can't be
// modified without recompilation.
//
// To read an available backend, use the Backend function. This ensures
// safe concurrent read access to the list of built-in backends.
//
// Backends are hardcoded into Terraform because the API for backends uses
// complex structures and supporting that over the plugin system is currently
// prohibitively difficult. For those wanting to implement a custom backend,
// they can do so with recompilation.
var backends map[string]func() backend.Backend
var backendsLock sync.Mutex

func init() {
// Our hardcoded backends. We don't need to acquire a lock here
// since init() code is serial and can't spawn goroutines.
backends = map[string]func() backend.Backend{
"local": func() backend.Backend { return &backendlocal.Local{} },
"consul": func() backend.Backend { return backendconsul.New() },
"inmem": func() backend.Backend { return backendinmem.New() },
}

// Add the legacy remote backends that haven't yet been convertd to
// the new backend API.
backendlegacy.Init(backends)
}

// Backend returns the initialization factory for the given backend, or
// nil if none exists.
func Backend(name string) func() backend.Backend {
backendsLock.Lock()
defer backendsLock.Unlock()
return backends[name]
}

// Set sets a new backend in the list of backends. If f is nil then the
// backend will be removed from the map. If this backend already exists
// then it will be overwritten.
//
// This method sets this backend globally and care should be taken to do
// this only before Terraform is executing to prevent odd behavior of backends
// changing mid-execution.
func Set(name string, f func() backend.Backend) {
backendsLock.Lock()
defer backendsLock.Unlock()

if f == nil {
delete(backends, name)
return
}

backends[name] = f
}
14 changes: 8 additions & 6 deletions backend/local/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"sync"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/state"
Expand Down Expand Up @@ -34,6 +33,9 @@ type Local struct {
StateOutPath string
StateBackupPath string

// we only want to create a single instance of the local state
state state.State

// ContextOpts are the base context options to set when initializing a
// Terraform context. Many of these will be overridden or merged by
// Operation. See Operation for more details.
Expand Down Expand Up @@ -100,17 +102,16 @@ func (b *Local) State() (state.State, error) {
return b.Backend.State()
}

if b.state != nil {
return b.state, nil
}

// Otherwise, we need to load the state.
var s state.State = &state.LocalState{
Path: b.StatePath,
PathOut: b.StateOutPath,
}

// Load the state as a sanity check
if err := s.RefreshState(); err != nil {
return nil, errwrap.Wrapf("Error reading local state: {{err}}", err)
}

// If we are backing up the state, wrap it
if path := b.StateBackupPath; path != "" {
s = &state.BackupState{
Expand All @@ -119,6 +120,7 @@ func (b *Local) State() (state.State, error) {
}
}

b.state = s
return s, nil
}

Expand Down
50 changes: 46 additions & 4 deletions backend/local/backend_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import (
"context"
"fmt"
"log"
"strings"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend"
clistate "github.com/hashicorp/terraform/command/state"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
)

Expand All @@ -17,6 +21,19 @@ func (b *Local) opApply(
runningOp *backend.RunningOperation) {
log.Printf("[INFO] backend/local: starting Apply operation")

// If we have a nil module at this point, then set it to an empty tree
// to avoid any potential crashes.
if op.Plan == nil && op.Module == nil && !op.Destroy {
runningOp.Err = fmt.Errorf(strings.TrimSpace(applyErrNoConfig))
return
}

// If we have a nil module at this point, then set it to an empty tree
// to avoid any potential crashes.
if op.Module == nil {
op.Module = module.NewEmptyTree()
}

// Setup our count hook that keeps track of resource changes
countHook := new(CountHook)
stateHook := new(StateHook)
Expand All @@ -28,12 +45,28 @@ func (b *Local) opApply(
b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook, stateHook)

// Get our context
tfCtx, state, err := b.context(op)
tfCtx, opState, err := b.context(op)
if err != nil {
runningOp.Err = err
return
}

if op.LockState {
lockInfo := state.NewLockInfo()
lockInfo.Operation = op.Type.String()
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
if err != nil {
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
return
}

defer func() {
if err := clistate.Unlock(opState, lockID, b.CLI, b.Colorize()); err != nil {
runningOp.Err = multierror.Append(runningOp.Err, err)
}
}()
}

// Setup the state
runningOp.State = tfCtx.State()

Expand All @@ -58,7 +91,7 @@ func (b *Local) opApply(
}

// Setup our hook for continuous state updates
stateHook.State = state
stateHook.State = opState

// Start the apply in a goroutine so that we can be interrupted.
var applyState *terraform.State
Expand Down Expand Up @@ -98,11 +131,11 @@ func (b *Local) opApply(
runningOp.State = applyState

// Persist the state
if err := state.WriteState(applyState); err != nil {
if err := opState.WriteState(applyState); err != nil {
runningOp.Err = fmt.Errorf("Failed to save state: %s", err)
return
}
if err := state.PersistState(); err != nil {
if err := opState.PersistState(); err != nil {
runningOp.Err = fmt.Errorf("Failed to save state: %s", err)
return
}
Expand Down Expand Up @@ -147,3 +180,12 @@ func (b *Local) opApply(
}
}
}

const applyErrNoConfig = `
No configuration files found!
Apply requires configuration to be present. Applying without a configuration
would mark everything for destruction, which is normally not what is desired.
If you would like to destroy everything, please run 'terraform destroy' instead
which does not require any configuration files.
`
54 changes: 54 additions & 0 deletions backend/local/backend_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package local
import (
"context"
"fmt"
"os"
"sync"
"testing"

Expand Down Expand Up @@ -50,6 +51,59 @@ test_instance.foo:
`)
}

func TestLocal_applyEmptyDir(t *testing.T) {
b := TestLocal(t)
p := TestLocalProvider(t, b, "test")

p.ApplyReturn = &terraform.InstanceState{ID: "yes"}

op := testOperationApply()
op.Module = nil

run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("bad: %s", err)
}
<-run.Done()
if run.Err == nil {
t.Fatal("should error")
}

if p.ApplyCalled {
t.Fatal("apply should not be called")
}

if _, err := os.Stat(b.StateOutPath); err == nil {
t.Fatal("should not exist")
}
}

func TestLocal_applyEmptyDirDestroy(t *testing.T) {
b := TestLocal(t)
p := TestLocalProvider(t, b, "test")

p.ApplyReturn = nil

op := testOperationApply()
op.Module = nil
op.Destroy = true

run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("bad: %s", err)
}
<-run.Done()
if run.Err != nil {
t.Fatalf("err: %s", err)
}

if p.ApplyCalled {
t.Fatal("apply should not be called")
}

checkState(t, b.StateOutPath, `<no state>`)
}

func TestLocal_applyError(t *testing.T) {
b := TestLocal(t)
p := TestLocalProvider(t, b, "test")
Expand Down
Loading

0 comments on commit 829a8a7

Please sign in to comment.