mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
While we're planning we must always update the state with the proposed new data resulting from the plan. In this case, we must record that the orphan instance doesn't exist at all in the proposed new state by storing its state as nil. This in turn allows references to the containing resource to evaluate properly, using the new updated resource count. This fixes TestContext2Apply_multiVarCountDec. This also includes a number of changes to the test output of TestContext2Apply_multiVarCountDec that make it easier to debug failures.
9816 lines
225 KiB
Go
9816 lines
225 KiB
Go
package terraform
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/go-test/deep"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/config/configschema"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
func TestContext2Apply_basic(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) < 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_unstable(t *testing.T) {
|
|
// This tests behavior when the configuration contains an unstable value,
|
|
// such as the result of uuid() or timestamp(), where each call produces
|
|
// a different result.
|
|
//
|
|
// This is an important case to test because we need to ensure that
|
|
// we don't re-call the function during the apply phase: the value should
|
|
// be fixed during plan
|
|
|
|
m := testModule(t, "apply-unstable")
|
|
p := testProvider("test")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"test": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected error during Plan: %s", diags.Err())
|
|
}
|
|
|
|
md := plan.Diff.RootModule()
|
|
rd := md.Resources["test_resource.foo"]
|
|
|
|
randomVal := rd.Attributes["random"].New
|
|
t.Logf("plan-time value is %q", randomVal)
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected error during Apply: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources))
|
|
}
|
|
|
|
rs := mod.Resources["test_resource.foo"].Primary
|
|
if got, want := rs.Attributes["random"], randomVal; got != want {
|
|
// FIXME: We actually currently have a bug where we re-interpolate
|
|
// the config during apply and end up with a random result, so this
|
|
// check fails. This check _should not_ fail, so we should fix this
|
|
// up in a later release.
|
|
//t.Errorf("wrong random value %q; want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_escape(t *testing.T) {
|
|
m := testModule(t, "apply-escape")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = "bar"
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_resourceCountOneList(t *testing.T) {
|
|
m := testModule(t, "apply-resource-count-one-list")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`null_resource.foo:
|
|
ID = foo
|
|
provider = provider.null
|
|
|
|
Outputs:
|
|
|
|
test = [foo]`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
|
|
}
|
|
}
|
|
func TestContext2Apply_resourceCountZeroList(t *testing.T) {
|
|
m := testModule(t, "apply-resource-count-zero-list")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`<no state>
|
|
Outputs:
|
|
|
|
test = []`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_resourceDependsOnModule(t *testing.T) {
|
|
m := testModule(t, "apply-resource-depends-on-module")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
// verify the apply happens in the correct order
|
|
var mu sync.Mutex
|
|
var order []string
|
|
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
if info.HumanId() == "module.child.aws_instance.child" {
|
|
|
|
// make the child slower than the parent
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
mu.Lock()
|
|
order = append(order, "child")
|
|
mu.Unlock()
|
|
} else {
|
|
mu.Lock()
|
|
order = append(order, "parent")
|
|
mu.Unlock()
|
|
}
|
|
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !reflect.DeepEqual(order, []string{"child", "parent"}) {
|
|
t.Fatal("resources applied out of order")
|
|
}
|
|
|
|
checkStateString(t, state, testTerraformApplyResourceDependsOnModuleStr)
|
|
}
|
|
|
|
// Test that without a config, the Dependencies in the state are enough
|
|
// to maintain proper ordering.
|
|
func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
|
|
m := testModule(t, "apply-resource-depends-on-module-empty")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Dependencies: []string{"module.child"},
|
|
},
|
|
},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.child": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
{
|
|
// verify the apply happens in the correct order
|
|
var mu sync.Mutex
|
|
var order []string
|
|
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
if info.HumanId() == "aws_instance.a" {
|
|
|
|
// make the dep slower than the parent
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
mu.Lock()
|
|
order = append(order, "child")
|
|
mu.Unlock()
|
|
} else {
|
|
mu.Lock()
|
|
order = append(order, "parent")
|
|
mu.Unlock()
|
|
}
|
|
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !reflect.DeepEqual(order, []string{"child", "parent"}) {
|
|
t.Fatal("resources applied out of order")
|
|
}
|
|
|
|
checkStateString(t, state, "<no state>")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-resource-depends-on-module")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
var globalState *State
|
|
{
|
|
p.ApplyFn = testApplyFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
globalState = state
|
|
}
|
|
|
|
{
|
|
// Wait for the dependency, sleep, and verify the graph never
|
|
// called a child.
|
|
var called int32
|
|
var checked bool
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
if info.HumanId() == "aws_instance.a" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 0 {
|
|
return nil, fmt.Errorf("module child should not be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: globalState,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should check")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
`)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_resourceDependsOnModuleGrandchild(t *testing.T) {
|
|
m := testModule(t, "apply-resource-depends-on-module-deep")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
{
|
|
// Wait for the dependency, sleep, and verify the graph never
|
|
// called a child.
|
|
var called int32
|
|
var checked bool
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
if info.HumanId() == "module.child.module.grandchild.aws_instance.c" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 0 {
|
|
return nil, fmt.Errorf("aws_instance.a should not be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should check")
|
|
}
|
|
|
|
checkStateString(t, state, testTerraformApplyResourceDependsOnModuleDeepStr)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) {
|
|
m := testModule(t, "apply-resource-depends-on-module-in-module")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
{
|
|
// Wait for the dependency, sleep, and verify the graph never
|
|
// called a child.
|
|
var called int32
|
|
var checked bool
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
if info.HumanId() == "module.child.module.grandchild.aws_instance.c" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 0 {
|
|
return nil, fmt.Errorf("nothing else should not be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should check")
|
|
}
|
|
|
|
checkStateString(t, state, testTerraformApplyResourceDependsOnModuleInModuleStr)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_mapVarBetweenModules(t *testing.T) {
|
|
m := testModule(t, "apply-map-var-through-module")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`<no state>
|
|
Outputs:
|
|
|
|
amis_from_module = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }
|
|
|
|
module.test:
|
|
null_resource.noop:
|
|
ID = foo
|
|
provider = provider.null
|
|
|
|
Outputs:
|
|
|
|
amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_refCount(t *testing.T) {
|
|
m := testModule(t, "apply-ref-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) < 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyRefCountStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_providerAlias(t *testing.T) {
|
|
m := testModule(t, "apply-provider-alias")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) < 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProviderAliasStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
// Two providers that are configured should both be configured prior to apply
|
|
func TestContext2Apply_providerAliasConfigure(t *testing.T) {
|
|
m := testModule(t, "apply-provider-alias-configure")
|
|
|
|
p2 := testProvider("another")
|
|
p2.ApplyFn = testApplyFn
|
|
p2.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"another": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
// Configure to record calls AFTER Plan above
|
|
var configCount int32
|
|
p2.ConfigureFn = func(c *ResourceConfig) error {
|
|
atomic.AddInt32(&configCount, 1)
|
|
|
|
foo, ok := c.Get("foo")
|
|
if !ok {
|
|
return fmt.Errorf("foo is not found")
|
|
}
|
|
|
|
if foo != "bar" {
|
|
return fmt.Errorf("foo: %#v", foo)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if configCount != 2 {
|
|
t.Fatalf("provider config expected 2 calls, got: %d", configCount)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProviderAliasConfigStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
// GH-2870
|
|
func TestContext2Apply_providerWarning(t *testing.T) {
|
|
m := testModule(t, "apply-provider-warning")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
p.ValidateFn = func(c *ResourceConfig) (ws []string, es []error) {
|
|
ws = append(ws, "Just a warning")
|
|
return
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected)
|
|
}
|
|
|
|
if !p.ConfigureCalled {
|
|
t.Fatalf("provider Configure() was never called!")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_emptyModule(t *testing.T) {
|
|
m := testModule(t, "apply-empty-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
actual = strings.Replace(actual, " ", "", -1)
|
|
expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_createBeforeDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-good-create-before")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("bad: %s", state)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) {
|
|
m := testModule(t, "apply-good-create-before-update")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("bad: %s", state)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCreateBeforeUpdateStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
// This tests that when a CBD resource depends on a non-CBD resource,
|
|
// we can still properly apply changes that require new for both.
|
|
func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) {
|
|
m := testModule(t, "apply-cbd-depends-non-cbd")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = foo
|
|
provider = provider.aws
|
|
require_new = yes
|
|
type = aws_instance
|
|
value = foo
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
require_new = yes
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) {
|
|
h := new(MockHook)
|
|
m := testModule(t, "apply-good-create-before")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var actual []string
|
|
var actualLock sync.Mutex
|
|
h.PostApplyFn = func(n *InstanceInfo, s *InstanceState, e error) (HookAction, error) {
|
|
actualLock.Lock()
|
|
defer actualLock.Unlock()
|
|
actual = append(actual, n.Id)
|
|
return HookActionContinue, nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
expected := []string{"aws_instance.bar", "aws_instance.bar (deposed #0)"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
// Test that we can perform an apply with CBD in a count with deposed instances.
|
|
func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) {
|
|
m := testModule(t, "apply-cbd-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Tainted: true,
|
|
},
|
|
|
|
Deposed: []*InstanceState{
|
|
&InstanceState{
|
|
ID: "foo",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.bar.1": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Tainted: true,
|
|
},
|
|
|
|
Deposed: []*InstanceState{
|
|
&InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar.0:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = bar
|
|
type = aws_instance
|
|
aws_instance.bar.1:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = bar
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
// Test that when we have a deposed instance but a good primary, we still
|
|
// destroy the deposed instance.
|
|
func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) {
|
|
m := testModule(t, "apply-cbd-deposed-only")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
|
|
Deposed: []*InstanceState{
|
|
&InstanceState{
|
|
ID: "foo",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = bar
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_destroyComputed(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-computed")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"output": "value",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
} else {
|
|
t.Logf("plan:\n\n%s", p.String())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// Test that the destroy operation uses depends_on as a source of ordering.
|
|
func TestContext2Apply_destroyDependsOn(t *testing.T) {
|
|
// It is possible for this to be racy, so we loop a number of times
|
|
// just to check.
|
|
for i := 0; i < 10; i++ {
|
|
testContext2Apply_destroyDependsOn(t)
|
|
}
|
|
}
|
|
|
|
func testContext2Apply_destroyDependsOn(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-depends-on")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Record the order we see Apply
|
|
var actual []string
|
|
var actualLock sync.Mutex
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo, _ *InstanceState, _ *InstanceDiff) (*InstanceState, error) {
|
|
actualLock.Lock()
|
|
defer actualLock.Unlock()
|
|
actual = append(actual, info.Id)
|
|
return nil, nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
Parallelism: 1, // To check ordering
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
expected := []string{"aws_instance.foo", "aws_instance.bar"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
// Test that destroy ordering is correct with dependencies only
|
|
// in the state.
|
|
func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
|
|
// It is possible for this to be racy, so we loop a number of times
|
|
// just to check.
|
|
for i := 0; i < 10; i++ {
|
|
testContext2Apply_destroyDependsOnStateOnly(t)
|
|
}
|
|
}
|
|
|
|
func testContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
|
|
m := testModule(t, "empty")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{},
|
|
},
|
|
Dependencies: []string{"aws_instance.foo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Record the order we see Apply
|
|
var actual []string
|
|
var actualLock sync.Mutex
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo, _ *InstanceState, _ *InstanceDiff) (*InstanceState, error) {
|
|
actualLock.Lock()
|
|
defer actualLock.Unlock()
|
|
actual = append(actual, info.Id)
|
|
return nil, nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
Parallelism: 1, // To check ordering
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
expected := []string{"aws_instance.bar", "aws_instance.foo"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
// Test that destroy ordering is correct with dependencies only
|
|
// in the state within a module (GH-11749)
|
|
func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
|
|
// It is possible for this to be racy, so we loop a number of times
|
|
// just to check.
|
|
for i := 0; i < 10; i++ {
|
|
testContext2Apply_destroyDependsOnStateOnlyModule(t)
|
|
}
|
|
}
|
|
|
|
func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
|
|
m := testModule(t, "empty")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{},
|
|
},
|
|
Dependencies: []string{"aws_instance.foo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Record the order we see Apply
|
|
var actual []string
|
|
var actualLock sync.Mutex
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo, _ *InstanceState, _ *InstanceDiff) (*InstanceState, error) {
|
|
actualLock.Lock()
|
|
defer actualLock.Unlock()
|
|
actual = append(actual, info.Id)
|
|
return nil, nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
Parallelism: 1, // To check ordering
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
expected := []string{"aws_instance.bar", "aws_instance.foo"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_dataBasic(t *testing.T) {
|
|
m := testModule(t, "apply-data-basic")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
p.ReadDataApplyReturn = &InstanceState{ID: "yo"}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDataBasicStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyData(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-data-resource")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"data.null_data_source.testing": &ResourceState{
|
|
Type: "null_data_source",
|
|
Primary: &InstanceState{
|
|
ID: "-",
|
|
Attributes: map[string]string{
|
|
"inputs.#": "1",
|
|
"inputs.test": "yes",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
hook := &testHook{}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
Hooks: []Hook{hook},
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
newState, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if got := len(newState.Modules); got != 1 {
|
|
t.Fatalf("state has %d modules after destroy; want 1", got)
|
|
}
|
|
|
|
if got := len(newState.Modules[0].Resources); got != 0 {
|
|
t.Fatalf("state has %d resources after destroy; want 0", got)
|
|
}
|
|
|
|
wantHookCalls := []*testHookCall{
|
|
{"PreDiff", "data.null_data_source.testing"},
|
|
{"PostDiff", "data.null_data_source.testing"},
|
|
{"PostStateUpdate", ""},
|
|
}
|
|
if !reflect.DeepEqual(hook.Calls, wantHookCalls) {
|
|
t.Errorf("wrong hook calls\ngot: %swant: %s", spew.Sdump(hook.Calls), spew.Sdump(wantHookCalls))
|
|
}
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/pull/5096
|
|
func TestContext2Apply_destroySkipsCBD(t *testing.T) {
|
|
// Config contains CBD resource depending on non-CBD resource, which triggers
|
|
// a cycle if they are both replaced, but should _not_ trigger a cycle when
|
|
// just doing a `terraform destroy`.
|
|
m := testModule(t, "apply-destroy-cbd")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
},
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-mod-var-provider-config")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
_, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/issues/2892
|
|
func TestContext2Apply_destroyCrossProviders(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-cross-providers")
|
|
|
|
p_aws := testProvider("aws")
|
|
p_aws.ApplyFn = testApplyFn
|
|
p_aws.DiffFn = testDiffFn
|
|
|
|
providers := map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p_aws),
|
|
}
|
|
|
|
// Bug only appears from time to time,
|
|
// so we run this test multiple times
|
|
// to check for the race-condition
|
|
|
|
// FIXME: this test flaps now, so run it more times
|
|
for i := 0; i <= 100; i++ {
|
|
ctx := getContextForApply_destroyCrossProviders(t, m, providers)
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
}
|
|
}
|
|
|
|
func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, providers map[string]ResourceProviderFactory) *Context {
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.shared": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "remote-2652591293",
|
|
Attributes: map[string]string{
|
|
"id": "test",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_vpc.bar": &ResourceState{
|
|
Type: "aws_vpc",
|
|
Primary: &InstanceState{
|
|
ID: "vpc-aaabbb12",
|
|
Attributes: map[string]string{
|
|
"value": "test",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(providers),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
return ctx
|
|
}
|
|
|
|
func TestContext2Apply_minimal(t *testing.T) {
|
|
m := testModule(t, "apply-minimal")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyMinimalStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_badDiff(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"newp": &ResourceAttrDiff{
|
|
Old: "",
|
|
New: "",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_cancel(t *testing.T) {
|
|
stopped := false
|
|
|
|
m := testModule(t, "apply-cancel")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) {
|
|
if !stopped {
|
|
stopped = true
|
|
go ctx.Stop()
|
|
|
|
for {
|
|
if ctx.sh.Stopped() {
|
|
break
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
return &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"value": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"value": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
// Start the Apply in a goroutine
|
|
var applyDiags tfdiags.Diagnostics
|
|
stateCh := make(chan *State)
|
|
go func() {
|
|
state, diags := ctx.Apply()
|
|
applyDiags = diags
|
|
|
|
stateCh <- state
|
|
}()
|
|
|
|
state := <-stateCh
|
|
if applyDiags.HasErrors() {
|
|
t.Fatalf("unexpected errors: %s", applyDiags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("bad: %s", state.String())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCancelStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
if !p.StopCalled {
|
|
t.Fatal("stop should be called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_cancelBlock(t *testing.T) {
|
|
m := testModule(t, "apply-cancel-block")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
applyCh := make(chan struct{})
|
|
p.DiffFn = testDiffFn
|
|
p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) {
|
|
close(applyCh)
|
|
|
|
for !ctx.sh.Stopped() {
|
|
// Wait for stop to be called. We call Gosched here so that
|
|
// the other goroutines can always be scheduled to set Stopped.
|
|
runtime.Gosched()
|
|
}
|
|
|
|
// Sleep
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
return &InstanceState{
|
|
ID: "foo",
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
// Start the Apply in a goroutine
|
|
var applyDiags tfdiags.Diagnostics
|
|
stateCh := make(chan *State)
|
|
go func() {
|
|
state, diags := ctx.Apply()
|
|
applyDiags = diags
|
|
|
|
stateCh <- state
|
|
}()
|
|
|
|
stopDone := make(chan struct{})
|
|
go func() {
|
|
defer close(stopDone)
|
|
<-applyCh
|
|
ctx.Stop()
|
|
}()
|
|
|
|
// Make sure that stop blocks
|
|
select {
|
|
case <-stopDone:
|
|
t.Fatal("stop should block")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
|
|
// Wait for stop
|
|
select {
|
|
case <-stopDone:
|
|
case <-time.After(500 * time.Millisecond):
|
|
t.Fatal("stop should be done")
|
|
}
|
|
|
|
// Wait for apply to complete
|
|
state := <-stateCh
|
|
if applyDiags.HasErrors() {
|
|
t.Fatalf("unexpected error: %s", applyDiags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_cancelProvisioner(t *testing.T) {
|
|
m := testModule(t, "apply-cancel-provisioner")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr := testProvisioner()
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
prStopped := make(chan struct{})
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
// Start the stop process
|
|
go ctx.Stop()
|
|
|
|
<-prStopped
|
|
return nil
|
|
}
|
|
pr.StopFn = func() error {
|
|
close(prStopped)
|
|
return nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
// Start the Apply in a goroutine
|
|
var applyDiags tfdiags.Diagnostics
|
|
stateCh := make(chan *State)
|
|
go func() {
|
|
state, diags := ctx.Apply()
|
|
applyDiags = diags
|
|
|
|
stateCh <- state
|
|
}()
|
|
|
|
// Wait for completion
|
|
state := <-stateCh
|
|
if applyDiags.HasErrors() {
|
|
t.Fatalf("unexpected errors: %s", applyDiags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo: (tainted)
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
|
|
if !pr.StopCalled {
|
|
t.Fatal("stop should be called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_compute(t *testing.T) {
|
|
m := testModule(t, "apply-compute")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
ctx.variables = InputValues{
|
|
"value": &InputValue{
|
|
Value: cty.NumberIntVal(1),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected errors: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyComputeStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_countDecrease(t *testing.T) {
|
|
m := testModule(t, "apply-count-dec")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.1": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.2": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_countDecreaseToOneX(t *testing.T) {
|
|
m := testModule(t, "apply-count-dec-one")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.1": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
"aws_instance.foo.2": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
// https://github.com/PeoplePerHour/terraform/pull/11
|
|
//
|
|
// This tests a case where both a "resource" and "resource.0" are in
|
|
// the state file, which apparently is a reasonable backwards compatibility
|
|
// concern found in the above 3rd party repo.
|
|
func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) {
|
|
m := testModule(t, "apply-count-dec-one")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
testStringMatch(t, p, testTerraformApplyCountDecToOneCorruptedPlanStr)
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_countTainted(t *testing.T) {
|
|
m := testModule(t, "apply-count-tainted")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "foo",
|
|
"type": "aws_instance",
|
|
},
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountTaintedStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_countVariable(t *testing.T) {
|
|
m := testModule(t, "apply-count-variable")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountVariableStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_countVariableRef(t *testing.T) {
|
|
m := testModule(t, "apply-count-variable-ref")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerInterpCount(t *testing.T) {
|
|
// This test ensures that a provisioner can interpolate a resource count
|
|
// even though the provisioner expression is evaluated during the plan
|
|
// walk. https://github.com/hashicorp/terraform/issues/16840
|
|
|
|
m := testModule(t, "apply-provisioner-interp-count")
|
|
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
|
|
providerResolver := ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
)
|
|
provisioners := map[string]ResourceProvisionerFactory{
|
|
"local-exec": testProvisionerFuncFixed(pr),
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: providerResolver,
|
|
Provisioners: provisioners,
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("plan failed unexpectedly: %s", diags.Err())
|
|
}
|
|
|
|
// We'll marshal and unmarshal the plan here, to ensure that we have
|
|
// a clean new context as would be created if we separately ran
|
|
// terraform plan -out=tfplan && terraform apply tfplan
|
|
var planBuf bytes.Buffer
|
|
err := WritePlan(plan, &planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to write plan: %s", err)
|
|
}
|
|
plan, err = ReadPlan(&planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to read plan: %s", err)
|
|
}
|
|
|
|
ctx, diags = plan.Context(&ContextOpts{
|
|
// Most options are taken from the plan in this case, but we still
|
|
// need to provide the plugins.
|
|
ProviderResolver: providerResolver,
|
|
Provisioners: provisioners,
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("failed to create context for plan: %s", diags.Err())
|
|
}
|
|
|
|
// Applying the plan should now succeed
|
|
_, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply failed unexpectedly: %s", diags.Err())
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner was not applied")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleBasic(t *testing.T) {
|
|
m := testModule(t, "apply-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad, expected:\n%s\n\nactual:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleDestroyOrder(t *testing.T) {
|
|
m := testModule(t, "apply-module-destroy-order")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
// Create a custom apply function to track the order they were destroyed
|
|
var order []string
|
|
var orderLock sync.Mutex
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
orderLock.Lock()
|
|
defer orderLock.Unlock()
|
|
|
|
order = append(order, is.ID)
|
|
return nil, nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.b": resourceState("aws_instance", "b"),
|
|
},
|
|
},
|
|
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": resourceState("aws_instance", "a"),
|
|
},
|
|
Outputs: map[string]*OutputState{
|
|
"a_output": &OutputState{
|
|
Type: "string",
|
|
Sensitive: false,
|
|
Value: "a",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
expected := []string{"b", "a"}
|
|
if !reflect.DeepEqual(order, expected) {
|
|
t.Fatalf("bad: %#v", order)
|
|
}
|
|
|
|
{
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleDestroyOrderStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleInheritAlias(t *testing.T) {
|
|
m := testModule(t, "apply-module-provider-inherit-alias")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if _, ok := c.Get("value"); !ok {
|
|
return nil
|
|
}
|
|
|
|
if _, ok := c.Get("root"); ok {
|
|
return fmt.Errorf("child should not get root")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws.eu
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) {
|
|
m := testModule(t, "apply-module-provider-inherit-alias-orphan")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
called := false
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
called = true
|
|
|
|
if _, ok := c.Get("child"); !ok {
|
|
return nil
|
|
}
|
|
|
|
if _, ok := c.Get("root"); ok {
|
|
return fmt.Errorf("child should not get root")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create a state with an orphan module
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Provider: "aws.eu",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !called {
|
|
t.Fatal("must call configure")
|
|
}
|
|
|
|
checkStateString(t, state, "")
|
|
}
|
|
|
|
func TestContext2Apply_moduleOrphanProvider(t *testing.T) {
|
|
m := testModule(t, "apply-module-orphan-provider-inherit")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if _, ok := c.Get("value"); !ok {
|
|
return fmt.Errorf("value is not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create a state with an orphan module
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) {
|
|
m := testModule(t, "apply-module-orphan-provider-inherit")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if _, ok := c.Get("value"); !ok {
|
|
return fmt.Errorf("value is not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create a state with an orphan module that is nested (grandchild)
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "parent", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleGrandchildProvider(t *testing.T) {
|
|
m := testModule(t, "apply-module-grandchild-provider-inherit")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var callLock sync.Mutex
|
|
called := false
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if _, ok := c.Get("value"); !ok {
|
|
return fmt.Errorf("value is not found")
|
|
}
|
|
callLock.Lock()
|
|
called = true
|
|
callLock.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
callLock.Lock()
|
|
defer callLock.Unlock()
|
|
if called != true {
|
|
t.Fatalf("err: configure never called")
|
|
}
|
|
}
|
|
|
|
// This tests an issue where all the providers in a module but not
|
|
// in the root weren't being added to the root properly. In this test
|
|
// case: aws is explicitly added to root, but "test" should be added to.
|
|
// With the bug, it wasn't.
|
|
func TestContext2Apply_moduleOnlyProvider(t *testing.T) {
|
|
m := testModule(t, "apply-module-only-provider")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pTest := testProvider("test")
|
|
pTest.ApplyFn = testApplyFn
|
|
pTest.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"test": testProviderFuncFixed(pTest),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleProviderAlias(t *testing.T) {
|
|
m := testModule(t, "apply-module-provider-alias")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) {
|
|
m := testModule(t, "apply-module-provider-alias")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.AbsResource{
|
|
Module: addrs.RootModuleInstance,
|
|
Resource: addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "nonexistent",
|
|
Name: "thing",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleProviderCloseNested(t *testing.T) {
|
|
m := testModule(t, "apply-module-provider-close-nested")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child", "subchild"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// Tests that variables used as module vars that reference data that
|
|
// already exists in the state and requires no diff works properly. This
|
|
// fixes an issue faced where module variables were pruned because they were
|
|
// accessing "non-existent" resources (they existed, just not in the graph
|
|
// cause they weren't in the diff).
|
|
func TestContext2Apply_moduleVarRefExisting(t *testing.T) {
|
|
m := testModule(t, "apply-ref-existing")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleVarResourceCount(t *testing.T) {
|
|
m := testModule(t, "apply-module-var-resource-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(2),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(5),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// GH-819
|
|
func TestContext2Apply_moduleBool(t *testing.T) {
|
|
m := testModule(t, "apply-module-bool")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleBoolStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
// Tests that a module can be targeted and everything is properly created.
|
|
// This adds to the plan test to also just verify that apply works.
|
|
func TestContext2Apply_moduleTarget(t *testing.T) {
|
|
m := testModule(t, "plan-targeted-cross-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("B", addrs.NoKey),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.A:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = bar
|
|
type = aws_instance
|
|
|
|
Outputs:
|
|
|
|
value = foo
|
|
module.B:
|
|
aws_instance.bar:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = foo
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_multiProvider(t *testing.T) {
|
|
m := testModule(t, "apply-multi-provider")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pDO := testProvider("do")
|
|
pDO.ApplyFn = testApplyFn
|
|
pDO.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"do": testProviderFuncFixed(pDO),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) < 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyMultiProviderStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_multiProviderDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-multi-provider-destroy")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
p.GetSchemaReturn = &ProviderSchema{
|
|
Provider: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"addr": {Type: cty.String, Optional: true},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"aws_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.String, Optional: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
p2 := testProvider("vault")
|
|
p2.ApplyFn = testApplyFn
|
|
p2.DiffFn = testDiffFn
|
|
p2.GetSchemaReturn = &ProviderSchema{
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"vault_instance": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var state *State
|
|
|
|
// First, create the instances
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"vault": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("errors during create plan: %s", diags.Err())
|
|
}
|
|
|
|
s, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("errors during create apply: %s", diags.Err())
|
|
}
|
|
|
|
state = s
|
|
}
|
|
|
|
// Destroy them
|
|
{
|
|
// Verify that aws_instance.bar is destroyed first
|
|
var checked bool
|
|
var called int32
|
|
var lock sync.Mutex
|
|
applyFn := func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
if info.HumanId() == "aws_instance.bar" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 0 {
|
|
return nil, fmt.Errorf("nothing else should be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
// Set the apply functions
|
|
p.ApplyFn = applyFn
|
|
p2.ApplyFn = applyFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"vault": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("errors during destroy plan: %s", diags.Err())
|
|
}
|
|
|
|
s, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("errors during destroy apply: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should be checked")
|
|
}
|
|
|
|
state = s
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
}
|
|
|
|
// This is like the multiProviderDestroy test except it tests that
|
|
// dependent resources within a child module that inherit provider
|
|
// configuration are still destroyed first.
|
|
func TestContext2Apply_multiProviderDestroyChild(t *testing.T) {
|
|
m := testModule(t, "apply-multi-provider-destroy-child")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
p2 := testProvider("vault")
|
|
p2.ApplyFn = testApplyFn
|
|
p2.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
|
|
// First, create the instances
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"vault": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
s, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state = s
|
|
}
|
|
|
|
// Destroy them
|
|
{
|
|
// Verify that aws_instance.bar is destroyed first
|
|
var checked bool
|
|
var called int32
|
|
var lock sync.Mutex
|
|
applyFn := func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
if info.HumanId() == "module.child.aws_instance.bar" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 0 {
|
|
return nil, fmt.Errorf("nothing else should be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
// Set the apply functions
|
|
p.ApplyFn = applyFn
|
|
p2.ApplyFn = applyFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"vault": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
s, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should be checked")
|
|
}
|
|
|
|
state = s
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_multiVar(t *testing.T) {
|
|
m := testModule(t, "apply-multi-var")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// First, apply with a count of 3
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(3),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := state.RootModule().Outputs["output"]
|
|
expected := "bar0,bar1,bar2"
|
|
if actual == nil || actual.Value != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
t.Logf("Initial state: %s", state.String())
|
|
|
|
// Apply again, reduce the count to 1
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(1),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("End state: %s", state.String())
|
|
|
|
actual := state.RootModule().Outputs["output"]
|
|
if actual == nil {
|
|
t.Fatal("missing output")
|
|
}
|
|
|
|
expected := "bar0"
|
|
if actual.Value != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a holistic test of multi-var (aka "splat variable") handling
|
|
// across several different Terraform subsystems. This is here because
|
|
// historically there were quirky differences in handling across different
|
|
// parts of Terraform and so here we want to assert the expected behavior and
|
|
// ensure that it remains consistent in future.
|
|
func TestContext2Apply_multiVarComprehensive(t *testing.T) {
|
|
m := testModule(t, "apply-multi-var-comprehensive")
|
|
p := testProvider("test")
|
|
|
|
configs := map[string]*ResourceConfig{}
|
|
|
|
p.ApplyFn = testApplyFn
|
|
|
|
p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) {
|
|
configs[info.HumanId()] = c
|
|
|
|
// Return a minimal diff to make sure this resource gets included in
|
|
// the apply graph and thus the final state, but otherwise we're just
|
|
// gathering data for assertions.
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"id": &ResourceAttrDiff{
|
|
NewComputed: true,
|
|
},
|
|
"name": &ResourceAttrDiff{
|
|
New: info.HumanId(),
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
p.GetSchemaReturn = &ProviderSchema{
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"test_thing": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"source_id": {Type: cty.String, Optional: true},
|
|
"source_name": {Type: cty.String, Optional: true},
|
|
"first_source_id": {Type: cty.String, Optional: true},
|
|
"first_source_name": {Type: cty.String, Optional: true},
|
|
"source_ids": {Type: cty.List(cty.String), Optional: true},
|
|
"source_names": {Type: cty.List(cty.String), Optional: true},
|
|
"source_ids_from_func": {Type: cty.List(cty.String), Optional: true},
|
|
"source_names_from_func": {Type: cty.List(cty.String), Optional: true},
|
|
"source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
|
|
"source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
|
|
|
|
"id": {Type: cty.String, Computed: true},
|
|
"name": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// First, apply with a count of 3
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"test": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(3),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("error during plan: %s", diags.Err())
|
|
}
|
|
|
|
checkConfig := func(name string, want map[string]interface{}) {
|
|
got := configs[name].Config
|
|
t.Run("config for "+name, func(t *testing.T) {
|
|
for _, problem := range deep.Equal(got, want) {
|
|
t.Errorf(problem)
|
|
}
|
|
})
|
|
}
|
|
|
|
checkConfig("test_thing.multi_count_var.0", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_id": unknownValue(),
|
|
"source_name": "test_thing.source.0",
|
|
})
|
|
checkConfig("test_thing.multi_count_var.2", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_id": unknownValue(),
|
|
"source_name": "test_thing.source.2",
|
|
})
|
|
checkConfig("test_thing.multi_count_derived.0", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_id": unknownValue(),
|
|
"source_name": "test_thing.source.0",
|
|
})
|
|
checkConfig("test_thing.multi_count_derived.2", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_id": unknownValue(),
|
|
"source_name": "test_thing.source.2",
|
|
})
|
|
checkConfig("test_thing.whole_splat", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_ids": []interface{}{
|
|
unknownValue(),
|
|
unknownValue(),
|
|
unknownValue(),
|
|
},
|
|
"source_names": []interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
"source_ids_from_func": unknownValue(),
|
|
"source_names_from_func": []interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
|
|
"source_ids_wrapped": []interface{}{
|
|
[]interface{}{
|
|
unknownValue(),
|
|
unknownValue(),
|
|
unknownValue(),
|
|
},
|
|
},
|
|
"source_names_wrapped": []interface{}{
|
|
[]interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
},
|
|
|
|
"first_source_id": unknownValue(),
|
|
"first_source_name": "test_thing.source.0",
|
|
})
|
|
checkConfig("module.child.test_thing.whole_splat", map[string]interface{}{
|
|
"id": unknownValue(),
|
|
"name": unknownValue(),
|
|
"source_ids": []interface{}{
|
|
unknownValue(),
|
|
unknownValue(),
|
|
unknownValue(),
|
|
},
|
|
"source_names": []interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
|
|
"source_ids_wrapped": []interface{}{
|
|
[]interface{}{
|
|
unknownValue(),
|
|
unknownValue(),
|
|
unknownValue(),
|
|
},
|
|
},
|
|
"source_names_wrapped": []interface{}{
|
|
[]interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
},
|
|
})
|
|
|
|
t.Run("apply", func(t *testing.T) {
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("error during apply: %s", diags.Err())
|
|
}
|
|
|
|
want := map[string]interface{}{
|
|
"source_ids": []interface{}{"foo", "foo", "foo"},
|
|
"source_names": []interface{}{
|
|
"test_thing.source.0",
|
|
"test_thing.source.1",
|
|
"test_thing.source.2",
|
|
},
|
|
}
|
|
got := map[string]interface{}{}
|
|
for k, s := range state.RootModule().Outputs {
|
|
got[k] = s.Value
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf(
|
|
"wrong outputs\ngot: %s\nwant: %s",
|
|
spew.Sdump(got), spew.Sdump(want),
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test that multi-var (splat) access is ordered by count, not by
|
|
// value.
|
|
func TestContext2Apply_multiVarOrder(t *testing.T) {
|
|
m := testModule(t, "apply-multi-var-order")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// First, apply with a count of 3
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("State: %s", state.String())
|
|
|
|
actual := state.RootModule().Outputs["should-be-11"]
|
|
expected := "index-11"
|
|
if actual == nil || actual.Value != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// Test that multi-var (splat) access is ordered by count, not by
|
|
// value, through interpolations.
|
|
func TestContext2Apply_multiVarOrderInterp(t *testing.T) {
|
|
m := testModule(t, "apply-multi-var-order-interp")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// First, apply with a count of 3
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("State: %s", state.String())
|
|
|
|
actual := state.RootModule().Outputs["should-be-11"]
|
|
expected := "baz-index-11"
|
|
if actual == nil || actual.Value != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// Based on GH-10440 where a graph edge wasn't properly being created
|
|
// between a modified resource and a count instance being destroyed.
|
|
func TestContext2Apply_multiVarCountDec(t *testing.T) {
|
|
var s *State
|
|
|
|
// First create resources. Nothing sneaky here.
|
|
{
|
|
m := testModule(t, "apply-multi-var-count-dec")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(2),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
log.Print("\n========\nStep 1 Plan\n========")
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
log.Print("\n========\nStep 1 Apply\n========")
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("Step 1 state:\n%s", state)
|
|
|
|
s = state
|
|
}
|
|
|
|
// Decrease the count by 1 and verify that everything happens in the
|
|
// right order.
|
|
{
|
|
m := testModule(t, "apply-multi-var-count-dec")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// Verify that aws_instance.bar is modified first and nothing
|
|
// else happens at the same time.
|
|
var checked bool
|
|
var called int32
|
|
var lock sync.Mutex
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
if info.HumanId() == "aws_instance.bar" {
|
|
checked = true
|
|
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Verify that called is 0 (dep not called)
|
|
if atomic.LoadInt32(&called) != 1 {
|
|
return nil, fmt.Errorf("nothing else should be called")
|
|
}
|
|
}
|
|
|
|
atomic.AddInt32(&called, 1)
|
|
return testApplyFn(info, is, id)
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
State: s,
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"num": &InputValue{
|
|
Value: cty.NumberIntVal(1),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
log.Print("\n========\nStep 2 Plan\n========")
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("Step 2 plan:\n%s", plan)
|
|
|
|
log.Print("\n========\nStep 2 Apply\n========")
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !checked {
|
|
t.Error("apply never called")
|
|
}
|
|
|
|
t.Logf("Step 2 state:\n%s", state)
|
|
|
|
s = state
|
|
}
|
|
}
|
|
|
|
// Test that we can resolve a multi-var (splat) for the first resource
|
|
// created in a non-root module, which happens when the module state doesn't
|
|
// exist yet.
|
|
// https://github.com/hashicorp/terraform/issues/14438
|
|
func TestContext2Apply_multiVarMissingState(t *testing.T) {
|
|
m := testModule(t, "apply-multi-var-missing-state")
|
|
p := testProvider("test")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
p.GetSchemaReturn = &ProviderSchema{
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"test_thing": {
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"a_ids": {Type: cty.String, Optional: true},
|
|
"id": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// First, apply with a count of 3
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"test": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan failed: %s", diags.Err())
|
|
}
|
|
|
|
// Before the relevant bug was fixed, Tdiagsaform would panic during apply.
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply failed: %s", diags.Err())
|
|
}
|
|
|
|
// If we get here with no errors or panics then our test was successful.
|
|
}
|
|
|
|
func TestContext2Apply_nilDiff(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputDependsOn(t *testing.T) {
|
|
m := testModule(t, "apply-output-depends-on")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
{
|
|
// Create a custom apply function that sleeps a bit (to allow parallel
|
|
// graph execution) and then returns an error to force a partial state
|
|
// return. We then verify the output is NOT there.
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
// Sleep to allow parallel execution
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Return error to force partial state
|
|
return nil, fmt.Errorf("abcd")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if !diags.HasErrors() || !strings.Contains(diags.Err().Error(), "abcd") {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
}
|
|
|
|
{
|
|
// Create the standard apply function and verify we get the output
|
|
p.ApplyFn = testApplyFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
|
|
Outputs:
|
|
|
|
value = result
|
|
`)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputOrphan(t *testing.T) {
|
|
m := testModule(t, "apply-output-orphan")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Outputs: map[string]*OutputState{
|
|
"foo": &OutputState{
|
|
Type: "string",
|
|
Sensitive: false,
|
|
Value: "bar",
|
|
},
|
|
"bar": &OutputState{
|
|
Type: "string",
|
|
Sensitive: false,
|
|
Value: "baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputOrphanModule(t *testing.T) {
|
|
m := testModule(t, "apply-output-orphan-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Outputs: map[string]*OutputState{
|
|
"foo": &OutputState{
|
|
Type: "string",
|
|
Value: "bar",
|
|
},
|
|
"bar": &OutputState{
|
|
Type: "string",
|
|
Value: "baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state.DeepCopy(),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
|
}
|
|
|
|
// now apply with no module in the config, which should remove the
|
|
// remaining output
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: configs.NewEmptyConfig(),
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state.DeepCopy(),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual = strings.TrimSpace(state.String())
|
|
if actual != "" {
|
|
t.Fatalf("expected no state, got:\n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_providerComputedVar(t *testing.T) {
|
|
m := testModule(t, "apply-provider-computed")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pTest := testProvider("test")
|
|
pTest.ApplyFn = testApplyFn
|
|
pTest.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"test": testProviderFuncFixed(pTest),
|
|
},
|
|
),
|
|
})
|
|
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if c.IsComputed("value") {
|
|
return fmt.Errorf("value is computed")
|
|
}
|
|
|
|
v, ok := c.Get("value")
|
|
if !ok {
|
|
return fmt.Errorf("value is not found")
|
|
}
|
|
if v != "yes" {
|
|
return fmt.Errorf("value is not 'yes': %v", v)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_providerConfigureDisabled(t *testing.T) {
|
|
m := testModule(t, "apply-provider-configure-disabled")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
called := false
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
called = true
|
|
|
|
if _, ok := c.Get("value"); !ok {
|
|
return fmt.Errorf("value is not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !called {
|
|
t.Fatal("configure never called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerModule(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-module")
|
|
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
pr.GetConfigSchemaReturnSchema = &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.String, Optional: true},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_Provisioner_compute(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-compute")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "computed_value" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: InputValues{
|
|
"value": &InputValue{
|
|
Value: cty.NumberIntVal(1),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerCreateFail(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-fail-create")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.DiffFn = testDiffFn
|
|
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
is.ID = "foo"
|
|
return is, fmt.Errorf("error")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-fail-create")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.DiffFn = testDiffFn
|
|
|
|
p.ApplyFn = func(
|
|
info *InstanceInfo,
|
|
is *InstanceState,
|
|
id *InstanceDiff) (*InstanceState, error) {
|
|
return nil, fmt.Errorf("error")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerFail(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-fail")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr.ApplyFn = func(*InstanceState, *ResourceConfig) error {
|
|
return fmt.Errorf("EXPLOSION")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: InputValues{
|
|
"value": &InputValue{
|
|
Value: cty.NumberIntVal(1),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-fail-create-before")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(*InstanceState, *ResourceConfig) error {
|
|
return fmt.Errorf("EXPLOSION")
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\n:got\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_error_createBeforeDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-error-create-before")
|
|
p := testProvider("aws")
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) {
|
|
return nil, fmt.Errorf("error")
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s\n\nExpected:\n\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-error-create-before")
|
|
p := testProvider("aws")
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "abc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) {
|
|
// Fail the destroy!
|
|
if id.Destroy {
|
|
return is, fmt.Errorf("error")
|
|
}
|
|
|
|
// Create should work
|
|
is = &InstanceState{
|
|
ID: "foo",
|
|
}
|
|
return is, nil
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-multi-depose-create-before-destroy")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
ps := map[string]ResourceProviderFactory{"aws": testProviderFuncFixed(p)}
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.web": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{ID: "foo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(ps),
|
|
State: state,
|
|
})
|
|
createdInstanceId := "bar"
|
|
// Create works
|
|
createFunc := func(is *InstanceState) (*InstanceState, error) {
|
|
return &InstanceState{ID: createdInstanceId}, nil
|
|
}
|
|
// Destroy starts broken
|
|
destroyFunc := func(is *InstanceState) (*InstanceState, error) {
|
|
return is, fmt.Errorf("destroy failed")
|
|
}
|
|
p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) {
|
|
if id.Destroy {
|
|
return destroyFunc(is)
|
|
} else {
|
|
return createFunc(is)
|
|
}
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
// Destroy is broken, so even though CBD successfully replaces the instance,
|
|
// we'll have to save the Deposed instance to destroy later
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.web: (1 deposed)
|
|
ID = bar
|
|
provider = provider.aws
|
|
Deposed ID 1 = foo
|
|
`)
|
|
|
|
createdInstanceId = "baz"
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(ps),
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
// We're replacing the primary instance once again. Destroy is _still_
|
|
// broken, so the Deposed list gets longer
|
|
state, diags = ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.web: (2 deposed)
|
|
ID = baz
|
|
provider = provider.aws
|
|
Deposed ID 1 = foo
|
|
Deposed ID 2 = bar
|
|
`)
|
|
|
|
// Destroy partially fixed!
|
|
destroyFunc = func(is *InstanceState) (*InstanceState, error) {
|
|
if is.ID == "foo" || is.ID == "baz" {
|
|
return nil, nil
|
|
} else {
|
|
return is, fmt.Errorf("destroy partially failed")
|
|
}
|
|
}
|
|
|
|
createdInstanceId = "qux"
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
state, diags = ctx.Apply()
|
|
// Expect error because 1/2 of Deposed destroys failed
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
// foo and baz are now gone, bar sticks around
|
|
checkStateString(t, state, `
|
|
aws_instance.web: (1 deposed)
|
|
ID = qux
|
|
provider = provider.aws
|
|
Deposed ID 1 = bar
|
|
`)
|
|
|
|
// Destroy working fully!
|
|
destroyFunc = func(is *InstanceState) (*InstanceState, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
createdInstanceId = "quux"
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatal("should not have error:", diags.Err())
|
|
}
|
|
|
|
// And finally the state is clean
|
|
checkStateString(t, state, `
|
|
aws_instance.web:
|
|
ID = quux
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
// Verify that a normal provisioner with on_failure "continue" set won't
|
|
// taint the resource and continues executing.
|
|
func TestContext2Apply_provisionerFailContinue(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-fail-continue")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
return fmt.Errorf("provisioner error")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = bar
|
|
type = aws_instance
|
|
`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
// Verify that a normal provisioner with on_failure "continue" records
|
|
// the error with the hook.
|
|
func TestContext2Apply_provisionerFailContinueHook(t *testing.T) {
|
|
h := new(MockHook)
|
|
m := testModule(t, "apply-provisioner-fail-continue")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
return fmt.Errorf("provisioner error")
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !h.PostProvisionCalled {
|
|
t.Fatal("PostProvision not called")
|
|
}
|
|
if h.PostProvisionErrorArg == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "destroy" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
// Verify that on destroy provisioner failure, nothing happens to the instance
|
|
func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
return fmt.Errorf("provisioner error")
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = bar
|
|
`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
// Verify that on destroy provisioner failure with "continue" that
|
|
// we continue to the next provisioner.
|
|
func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-continue")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var l sync.Mutex
|
|
var calls []string
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
calls = append(calls, val.(string))
|
|
return fmt.Errorf("provisioner error")
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
expected := []string{"one", "two"}
|
|
if !reflect.DeepEqual(calls, expected) {
|
|
t.Fatalf("bad: %#v", calls)
|
|
}
|
|
}
|
|
|
|
// Verify that on destroy provisioner failure with "continue" that
|
|
// we continue to the next provisioner. But if the next provisioner defines
|
|
// to fail, then we fail after running it.
|
|
func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-fail")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var l sync.Mutex
|
|
var calls []string
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
calls = append(calls, val.(string))
|
|
return fmt.Errorf("provisioner error")
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = bar
|
|
`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
expected := []string{"one", "two"}
|
|
if !reflect.DeepEqual(calls, expected) {
|
|
t.Fatalf("bad: %#v", calls)
|
|
}
|
|
}
|
|
|
|
// Verify destroy provisioners are not run for tainted instances.
|
|
func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
destroyCalled := false
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
expected := "create"
|
|
if rs.ID == "bar" {
|
|
destroyCalled = true
|
|
return nil
|
|
}
|
|
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != expected {
|
|
t.Fatalf("bad value for command: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = bar
|
|
type = aws_instance
|
|
`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
if destroyCalled {
|
|
t.Fatal("destroy should not be called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerDestroyModule(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-module")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "value" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
module.child:
|
|
<no state>`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerDestroyRef(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "hello" {
|
|
return fmt.Errorf("bad value for command: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"value": "hello",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
// Test that a destroy provisioner referencing an invalid key errors.
|
|
func TestContext2Apply_provisionerDestroyRefInvalid(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
return nil
|
|
}
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Destroy: true,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerResourceRef(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-resource-ref")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "2" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerSelfRef(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-self-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "bar" {
|
|
t.Fatalf("bad value for command: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) {
|
|
var lock sync.Mutex
|
|
commands := make([]string, 0, 5)
|
|
|
|
m := testModule(t, "apply-provisioner-multi-self-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
val, ok := c.Config["command"]
|
|
if !ok {
|
|
t.Fatalf("bad value for command: %v %#v", val, c)
|
|
}
|
|
|
|
commands = append(commands, val.(string))
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
// Verify our result
|
|
sort.Strings(commands)
|
|
expectedCommands := []string{"number 0", "number 1", "number 2"}
|
|
if !reflect.DeepEqual(commands, expectedCommands) {
|
|
t.Fatalf("bad: %#v", commands)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) {
|
|
var lock sync.Mutex
|
|
order := make([]string, 0, 5)
|
|
|
|
m := testModule(t, "apply-provisioner-multi-self-ref-single")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
val, ok := c.Config["order"]
|
|
if !ok {
|
|
t.Fatalf("bad value for order: %v %#v", val, c)
|
|
}
|
|
|
|
order = append(order, val.(string))
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
// Verify our result
|
|
sort.Strings(order)
|
|
expectedOrder := []string{"0", "1", "2"}
|
|
if !reflect.DeepEqual(order, expectedOrder) {
|
|
t.Fatalf("bad: %#v", order)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-explicit-self-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["command"]
|
|
if !ok || val != "bar" {
|
|
t.Fatalf("bad value for command: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var state *State
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
_, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Destroy: true,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
_, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
}
|
|
}
|
|
|
|
// Provisioner should NOT run on a diff, only create
|
|
func TestContext2Apply_Provisioner_Diff(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-diff")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
return nil
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
pr.ApplyCalled = false
|
|
|
|
// Change the state to force a diff
|
|
mod := state.RootModule()
|
|
mod.Resources["aws_instance.bar"].Primary.Attributes["foo"] = "baz"
|
|
|
|
// Re-create context with state
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state2, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual = strings.TrimSpace(state2.String())
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was NOT invoked
|
|
if pr.ApplyCalled {
|
|
t.Fatalf("provisioner invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputDiffVars(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.baz": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
for k, ad := range d.Attributes {
|
|
if ad.NewComputed {
|
|
return nil, fmt.Errorf("%s: computed", k)
|
|
}
|
|
}
|
|
|
|
result := s.MergeDiff(d)
|
|
result.ID = "foo"
|
|
result.Attributes["foo"] = "bar"
|
|
return result, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"foo": &ResourceAttrDiff{
|
|
NewComputed: true,
|
|
Type: DiffAttrOutput,
|
|
},
|
|
"bar": &ResourceAttrDiff{
|
|
New: "baz",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_Provisioner_ConnInfo(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-conninfo")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
if s.Ephemeral.ConnInfo == nil {
|
|
t.Fatalf("ConnInfo not initialized")
|
|
}
|
|
|
|
result, _ := testApplyFn(info, s, d)
|
|
result.Ephemeral.ConnInfo = map[string]string{
|
|
"type": "ssh",
|
|
"host": "127.0.0.1",
|
|
"port": "22",
|
|
}
|
|
return result, nil
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr.GetConfigSchemaReturnSchema = &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.String, Optional: true},
|
|
},
|
|
}
|
|
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
conn := rs.Ephemeral.ConnInfo
|
|
if conn["type"] != "telnet" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
if conn["host"] != "127.0.0.1" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
if conn["port"] != "2222" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
if conn["user"] != "superuser" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
if conn["password"] != "test" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: InputValues{
|
|
"value": &InputValue{
|
|
Value: cty.StringVal("1"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
"pass": &InputValue{
|
|
Value: cty.StringVal("test"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyX(t *testing.T) {
|
|
m := testModule(t, "apply-destroy")
|
|
h := new(HookRecordApplyOrder)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Next, plan and apply a destroy operation
|
|
h.Active = true
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Test that things were destroyed _in the right order_
|
|
expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
|
|
actual2 := h.IDs
|
|
if !reflect.DeepEqual(actual2, expected2) {
|
|
t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyOrder(t *testing.T) {
|
|
m := testModule(t, "apply-destroy")
|
|
h := new(HookRecordApplyOrder)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("State 1: %s", state)
|
|
|
|
// Next, plan and apply a destroy
|
|
h.Active = true
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
// Test that things were destroyed _in the right order_
|
|
expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
|
|
actual2 := h.IDs
|
|
if !reflect.DeepEqual(actual2, expected2) {
|
|
t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
|
|
}
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/issues/2767
|
|
func TestContext2Apply_destroyModulePrefix(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-module-resource-prefix")
|
|
h := new(MockHook)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Verify that we got the apply info correct
|
|
if v := h.PreApplyInfo.HumanId(); v != "module.child.aws_instance.foo" {
|
|
t.Fatalf("bad: %s", v)
|
|
}
|
|
|
|
// Next, plan and apply a destroy operation and reset the hook
|
|
h = new(MockHook)
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
if v := h.PreApplyInfo.HumanId(); v != "module.child.aws_instance.foo" {
|
|
t.Fatalf("bad: %s", v)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyNestedModule(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-nested-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child", "subchild"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
if actual != "" {
|
|
t.Fatalf("expected no state, got: %s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-deeply-nested-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child", "subchild", "subsubchild"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
if actual != "" {
|
|
t.Fatalf("epected no state, got: %s", actual)
|
|
}
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/issues/5440
|
|
func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-module-with-attrs")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("Step 1 plan: %s", p)
|
|
}
|
|
|
|
var diags tfdiags.Diagnostics
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply errs: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("Step 1 state: %s", state)
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Config: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Variables: InputValues{
|
|
"key_name": &InputValue{
|
|
Value: cty.StringVal("foobarkey"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy plan err: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("Step 2 plan: %s", plan)
|
|
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("plan write err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("plan read err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy apply err: %s", diags.Err())
|
|
}
|
|
|
|
t.Logf("Step 2 state: %s", state)
|
|
}
|
|
|
|
//Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n\n%s\n\nactual:\n\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-mod-var-and-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
var diags tfdiags.Diagnostics
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Config: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy plan err: %s", diags.Err())
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("plan write err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("plan read err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
//Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-mod-var-and-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
var diags tfdiags.Diagnostics
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey),
|
|
},
|
|
})
|
|
|
|
_, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
}
|
|
|
|
// Destroy, targeting the module explicitly
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("destroy apply err: %s", err)
|
|
}
|
|
}
|
|
|
|
//Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-mod-var-and-count-nested")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
var diags tfdiags.Diagnostics
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Config: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy plan err: %s", diags.Err())
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("plan write err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("plan read err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
//Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
module.child:
|
|
<no state>
|
|
module.child.child2:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyOutputs(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-outputs")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// Next, plan and apply a destroy operation
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) > 0 {
|
|
t.Fatalf("expected no resources, got: %#v", mod)
|
|
}
|
|
|
|
// destroying again should produce no errors
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Module: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err = ctx.Apply(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyOrphan(t *testing.T) {
|
|
m := testModule(t, "apply-error")
|
|
p := testProvider("aws")
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.baz": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
if d.Destroy {
|
|
return nil, nil
|
|
}
|
|
|
|
result := s.MergeDiff(d)
|
|
result.ID = "foo"
|
|
return result, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"value": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if _, ok := mod.Resources["aws_instance.baz"]; ok {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-provisioner")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
called := false
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
called = true
|
|
return nil
|
|
}
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"id": "bar",
|
|
},
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: s,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if called {
|
|
t.Fatal("provisioner should not be called")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace("<no state>")
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_error(t *testing.T) {
|
|
errored := false
|
|
|
|
m := testModule(t, "apply-error")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) {
|
|
if errored {
|
|
state := &InstanceState{
|
|
ID: "bar",
|
|
}
|
|
return state, fmt.Errorf("error")
|
|
}
|
|
errored = true
|
|
|
|
return &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"value": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"value": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyErrorStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_errorPartial(t *testing.T) {
|
|
errored := false
|
|
|
|
m := testModule(t, "apply-error")
|
|
p := testProvider("aws")
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
if errored {
|
|
return s, fmt.Errorf("error")
|
|
}
|
|
errored = true
|
|
|
|
return &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"value": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"value": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyErrorPartialStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_hook(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
h := new(MockHook)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !h.PreApplyCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !h.PostApplyCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !h.PostStateUpdateCalled {
|
|
t.Fatalf("should call post state update")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_hookOrphan(t *testing.T) {
|
|
m := testModule(t, "apply-blank")
|
|
h := new(MockHook)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatalf("apply errors: %s", diags.Err())
|
|
}
|
|
|
|
if !h.PreApplyCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !h.PostApplyCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !h.PostStateUpdateCalled {
|
|
t.Fatalf("should call post state update")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_idAttr(t *testing.T) {
|
|
m := testModule(t, "apply-idattr")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
result := s.MergeDiff(d)
|
|
result.ID = "foo"
|
|
result.Attributes = map[string]string{
|
|
"id": "bar",
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"num": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
rs, ok := mod.Resources["aws_instance.foo"]
|
|
if !ok {
|
|
t.Fatal("not in state")
|
|
}
|
|
if rs.Primary.ID != "foo" {
|
|
t.Fatalf("bad: %#v", rs.Primary.ID)
|
|
}
|
|
if rs.Primary.Attributes["id"] != "foo" {
|
|
t.Fatalf("bad: %#v", rs.Primary.Attributes)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputBasic(t *testing.T) {
|
|
m := testModule(t, "apply-output")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputInvalid(t *testing.T) {
|
|
m := testModule(t, "apply-output-invalid")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
_, diags := ctx.Plan()
|
|
if !diags.HasErrors() {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if errStr := diags.Err().Error(); !strings.Contains(errStr, "is not a valid type") {
|
|
t.Fatalf("wrong error: %s", errStr)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputAdd(t *testing.T) {
|
|
m1 := testModule(t, "apply-output-add-before")
|
|
p1 := testProvider("aws")
|
|
p1.ApplyFn = testApplyFn
|
|
p1.DiffFn = testDiffFn
|
|
ctx1 := testContext2(t, &ContextOpts{
|
|
Config: m1,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p1),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx1.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state1, diags := ctx1.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
m2 := testModule(t, "apply-output-add-after")
|
|
p2 := testProvider("aws")
|
|
p2.ApplyFn = testApplyFn
|
|
p2.DiffFn = testDiffFn
|
|
ctx2 := testContext2(t, &ContextOpts{
|
|
Config: m2,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p2),
|
|
},
|
|
),
|
|
State: state1,
|
|
})
|
|
|
|
if _, diags := ctx2.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
state2, diags := ctx2.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state2.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputAddStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputList(t *testing.T) {
|
|
m := testModule(t, "apply-output-list")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputListStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputMulti(t *testing.T) {
|
|
m := testModule(t, "apply-output-multi")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputMultiStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_outputMultiIndex(t *testing.T) {
|
|
m := testModule(t, "apply-output-multi-index")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_taintX(t *testing.T) {
|
|
m := testModule(t, "apply-taint")
|
|
p := testProvider("aws")
|
|
// destroyCount tests against regression of
|
|
// https://github.com/hashicorp/terraform/issues/1056
|
|
var destroyCount = int32(0)
|
|
var once sync.Once
|
|
simulateProviderDelay := func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
once.Do(simulateProviderDelay)
|
|
if d.Destroy {
|
|
atomic.AddInt32(&destroyCount, 1)
|
|
}
|
|
return testApplyFn(info, s, d)
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"num": "2",
|
|
"type": "aws_instance",
|
|
},
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyTaintStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad:\n%s", actual)
|
|
}
|
|
|
|
if destroyCount != 1 {
|
|
t.Fatalf("Expected 1 destroy, got %d", destroyCount)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_taintDep(t *testing.T) {
|
|
m := testModule(t, "apply-taint-dep")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"num": "2",
|
|
"type": "aws_instance",
|
|
},
|
|
Tainted: true,
|
|
},
|
|
},
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "baz",
|
|
"num": "2",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyTaintDepStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad:\n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_taintDepRequiresNew(t *testing.T) {
|
|
m := testModule(t, "apply-taint-dep-requires-new")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"num": "2",
|
|
"type": "aws_instance",
|
|
},
|
|
Tainted: true,
|
|
},
|
|
},
|
|
"aws_instance.bar": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"foo": "baz",
|
|
"num": "2",
|
|
"type": "aws_instance",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyTaintDepRequireNewStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad:\n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_targeted(t *testing.T) {
|
|
m := testModule(t, "apply-targeted")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("expected 1 resource, got: %#v", mod.Resources)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetEmpty(t *testing.T) {
|
|
m := testModule(t, "apply-targeted")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []string{""},
|
|
})
|
|
|
|
_, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatalf("should error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_targetedCount(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo.0:
|
|
ID = foo
|
|
provider = provider.aws
|
|
aws_instance.foo.1:
|
|
ID = foo
|
|
provider = provider.aws
|
|
aws_instance.foo.2:
|
|
ID = foo
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedCountIndex(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.ResourceInstance(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1),
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo.1:
|
|
ID = foo
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-targeted")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("expected 1 resource, got: %#v", mod.Resources)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = i-abc123
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_destroyProvisionerWithLocals(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-locals")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
pr.ApplyFn = func(_ *InstanceState, rc *ResourceConfig) error {
|
|
cmd, ok := rc.Get("command")
|
|
if !ok || cmd != "local" {
|
|
return fmt.Errorf("provisioner got %v:%s", ok, cmd)
|
|
}
|
|
return nil
|
|
}
|
|
pr.GetConfigSchemaReturnSchema = &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"command": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
"when": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "1234"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Destroy: true,
|
|
// the test works without targeting, but this also tests that the local
|
|
// node isn't inadvertently pruned because of the wrong evaluation
|
|
// order.
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
if !pr.ApplyCalled {
|
|
t.Fatal("provisioner not called")
|
|
}
|
|
}
|
|
|
|
// this also tests a local value in the config referencing a resource that
|
|
// wasn't in the state during destroy.
|
|
func TestContext2Apply_destroyProvisionerWithMultipleLocals(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-multiple-locals")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
pr.GetConfigSchemaReturnSchema = &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"command": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
"when": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
pr.ApplyFn = func(is *InstanceState, rc *ResourceConfig) error {
|
|
cmd, ok := rc.Get("command")
|
|
if !ok {
|
|
return errors.New("no command in provisioner")
|
|
}
|
|
|
|
switch is.ID {
|
|
case "1234":
|
|
if cmd != "local" {
|
|
return fmt.Errorf("provisioner %q got:%q", is.ID, cmd)
|
|
}
|
|
case "3456":
|
|
if cmd != "1234" {
|
|
return fmt.Errorf("provisioner %q got:%q", is.ID, cmd)
|
|
}
|
|
default:
|
|
t.Fatal("unknown instance")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "1234"),
|
|
"aws_instance.bar": resourceState("aws_instance", "3456"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Apply(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
if !pr.ApplyCalled {
|
|
t.Fatal("provisioner not called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyProvisionerWithOutput(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-destroy-outputs")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
pr := testProvisioner()
|
|
pr.ApplyFn = func(is *InstanceState, rc *ResourceConfig) error {
|
|
cmd, ok := rc.Get("command")
|
|
if !ok || cmd != "3" {
|
|
return fmt.Errorf("provisioner for %s got %v:%s", is.ID, ok, cmd)
|
|
}
|
|
return nil
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "1"),
|
|
},
|
|
Outputs: map[string]*OutputState{
|
|
"value": {
|
|
Type: "string",
|
|
Value: "3",
|
|
},
|
|
},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "mod"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.baz": resourceState("aws_instance", "3"),
|
|
},
|
|
// state needs to be properly initialized
|
|
Outputs: map[string]*OutputState{},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "mod2"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.bar": resourceState("aws_instance", "2"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Destroy: true,
|
|
|
|
// targeting the source of the value used by all resources should still
|
|
// destroy them all.
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("mod", addrs.NoKey).Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "baz",
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
if !pr.ApplyCalled {
|
|
t.Fatal("provisioner not called")
|
|
}
|
|
|
|
// confirm all outputs were removed too
|
|
for _, mod := range state.Modules {
|
|
if len(mod.Outputs) > 0 {
|
|
t.Fatalf("output left in module state: %#v\n", mod)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-targeted-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/issues/4462
|
|
func TestContext2Apply_targetedDestroyModule(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
|
},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = i-abc123
|
|
provider = provider.aws
|
|
aws_instance.foo:
|
|
ID = i-bcd345
|
|
provider = provider.aws
|
|
|
|
module.child:
|
|
aws_instance.bar:
|
|
ID = i-abc123
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-count")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo.0": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.foo.1": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.foo.2": resourceState("aws_instance", "i-bcd345"),
|
|
"aws_instance.bar.0": resourceState("aws_instance", "i-abc123"),
|
|
"aws_instance.bar.1": resourceState("aws_instance", "i-abc123"),
|
|
"aws_instance.bar.2": resourceState("aws_instance", "i-abc123"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.ResourceInstance(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(2),
|
|
),
|
|
addrs.RootModuleInstance.ResourceInstance(
|
|
addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1),
|
|
),
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar.0:
|
|
ID = i-abc123
|
|
provider = provider.aws
|
|
aws_instance.bar.2:
|
|
ID = i-abc123
|
|
provider = provider.aws
|
|
aws_instance.foo.0:
|
|
ID = i-bcd345
|
|
provider = provider.aws
|
|
aws_instance.foo.1:
|
|
ID = i-bcd345
|
|
provider = provider.aws
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedModule(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.ModuleByPath(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
|
if mod == nil {
|
|
t.Fatalf("no child module found in the state!\n\n%#v", state)
|
|
}
|
|
if len(mod.Resources) != 2 {
|
|
t.Fatalf("expected 2 resources, got: %#v", mod.Resources)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
aws_instance.bar:
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
// GH-1858
|
|
func TestContext2Apply_targetedModuleDep(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module-dep")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("Diff: %s", p)
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
foo = foo
|
|
type = aws_instance
|
|
|
|
Dependencies:
|
|
module.child
|
|
|
|
module.child:
|
|
aws_instance.mod:
|
|
ID = foo
|
|
provider = provider.aws
|
|
|
|
Outputs:
|
|
|
|
output = foo
|
|
`)
|
|
}
|
|
|
|
// GH-10911 untargeted outputs should not be in the graph, and therefore
|
|
// not execute.
|
|
func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module-unrelated-outputs")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child2", addrs.NoKey),
|
|
},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
{
|
|
Path: []string{"root"},
|
|
Outputs: map[string]*OutputState{},
|
|
Resources: map[string]*ResourceState{},
|
|
},
|
|
{
|
|
Path: []string{"root", "child1"},
|
|
Outputs: map[string]*OutputState{
|
|
"instance_id": {
|
|
Type: "string",
|
|
Value: "foo-bar-baz",
|
|
},
|
|
},
|
|
Resources: map[string]*ResourceState{},
|
|
},
|
|
{
|
|
Path: []string{"root", "child2"},
|
|
Outputs: map[string]*OutputState{},
|
|
Resources: map[string]*ResourceState{},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
// module.child1's instance_id output should be retained from state
|
|
// module.child2's instance_id is updated because its dependency is updated
|
|
// child2_id is updated because if its transitive dependency via module.child2
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
Outputs:
|
|
|
|
child2_id = foo
|
|
|
|
module.child1:
|
|
<no state>
|
|
Outputs:
|
|
|
|
instance_id = foo-bar-baz
|
|
module.child2:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
|
|
Outputs:
|
|
|
|
instance_id = foo
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedModuleResource(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module-resource")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.ModuleByPath(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
|
if mod == nil || len(mod.Resources) != 1 {
|
|
t.Fatalf("expected 1 resource, got: %#v", mod)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_unknownAttribute(t *testing.T) {
|
|
m := testModule(t, "apply-unknown")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if !diags.HasErrors() {
|
|
t.Fatal("should error with UnknownVariableValue")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) {
|
|
m := testModule(t, "apply-unknown-interpolate")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_vars(t *testing.T) {
|
|
fixture := contextFixtureApplyVars(t)
|
|
opts := fixture.ContextOpts()
|
|
opts.Variables = InputValues{
|
|
"foo": &InputValue{
|
|
Value: cty.StringVal("us-east-1"),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
"test_list": &InputValue{
|
|
Value: cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.StringVal("World"),
|
|
}),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
"test_map": &InputValue{
|
|
Value: cty.MapVal(map[string]cty.Value{
|
|
"Hello": cty.StringVal("World"),
|
|
"Foo": cty.StringVal("Bar"),
|
|
"Baz": cty.StringVal("Foo"),
|
|
}),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
"amis": &InputValue{
|
|
Value: cty.MapVal(map[string]cty.Value{
|
|
"us-east-1": cty.StringVal("override"),
|
|
}),
|
|
SourceType: ValueFromCaller,
|
|
},
|
|
}
|
|
ctx := testContext2(t, opts)
|
|
|
|
diags := ctx.Validate()
|
|
if len(diags) != 0 {
|
|
t.Fatalf("bad: %s", diags.ErrWithWarnings())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyVarsStr)
|
|
if actual != expected {
|
|
t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_varsEnv(t *testing.T) {
|
|
fixture := contextFixtureApplyVarsEnv(t)
|
|
opts := fixture.ContextOpts()
|
|
opts.Variables = InputValues{
|
|
"string": &InputValue{
|
|
Value: cty.StringVal("baz"),
|
|
SourceType: ValueFromEnvVar,
|
|
},
|
|
"list": &InputValue{
|
|
Value: cty.ListVal([]cty.Value{
|
|
cty.StringVal("Hello"),
|
|
cty.StringVal("World"),
|
|
}),
|
|
SourceType: ValueFromEnvVar,
|
|
},
|
|
"map": &InputValue{
|
|
Value: cty.MapVal(map[string]cty.Value{
|
|
"Hello": cty.StringVal("World"),
|
|
"Foo": cty.StringVal("Bar"),
|
|
"Baz": cty.StringVal("Foo"),
|
|
}),
|
|
SourceType: ValueFromEnvVar,
|
|
},
|
|
}
|
|
ctx := testContext2(t, opts)
|
|
|
|
diags := ctx.Validate()
|
|
if len(diags) != 0 {
|
|
t.Fatalf("bad: %s", diags.ErrWithWarnings())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyVarsEnvStr)
|
|
if actual != expected {
|
|
t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_createBefore_depends(t *testing.T) {
|
|
m := testModule(t, "apply-depends-create-before")
|
|
h := new(HookRecordApplyOrder)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.web": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "ami-old",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.lb": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"instance": "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
h.Active = true
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) < 2 {
|
|
t.Fatalf("bad: %#v", mod.Resources)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s\n\n%s", actual, expected)
|
|
}
|
|
|
|
// Test that things were managed _in the right order_
|
|
order := h.States
|
|
diffs := h.Diffs
|
|
if order[0].ID != "" || diffs[0].Destroy {
|
|
t.Fatalf("should create new instance first: %#v", order)
|
|
}
|
|
|
|
if order[1].ID != "baz" {
|
|
t.Fatalf("update must happen after create: %#v", order)
|
|
}
|
|
|
|
if order[2].ID != "bar" || !diffs[2].Destroy {
|
|
t.Fatalf("destroy must happen after update: %#v", order)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_singleDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-depends-create-before")
|
|
h := new(HookRecordApplyOrder)
|
|
p := testProvider("aws")
|
|
invokeCount := 0
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
invokeCount++
|
|
switch invokeCount {
|
|
case 1:
|
|
if d.Destroy {
|
|
t.Fatalf("should not destroy")
|
|
}
|
|
if s.ID != "" {
|
|
t.Fatalf("should not have ID")
|
|
}
|
|
case 2:
|
|
if d.Destroy {
|
|
t.Fatalf("should not destroy")
|
|
}
|
|
if s.ID != "baz" {
|
|
t.Fatalf("should have id")
|
|
}
|
|
case 3:
|
|
if !d.Destroy {
|
|
t.Fatalf("should destroy")
|
|
}
|
|
if s.ID == "" {
|
|
t.Fatalf("should have ID")
|
|
}
|
|
default:
|
|
t.Fatalf("bad invoke count %d", invokeCount)
|
|
}
|
|
return testApplyFn(info, s, d)
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.web": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"require_new": "ami-old",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.lb": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "baz",
|
|
Attributes: map[string]string{
|
|
"instance": "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
Hooks: []Hook{h},
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
h.Active = true
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if invokeCount != 3 {
|
|
t.Fatalf("bad: %d", invokeCount)
|
|
}
|
|
}
|
|
|
|
// GH-7824
|
|
func TestContext2Apply_issue7824(t *testing.T) {
|
|
p := testProvider("template")
|
|
p.ResourcesReturn = append(p.ResourcesReturn, ResourceType{
|
|
Name: "template_file",
|
|
})
|
|
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// Apply cleanly step 0
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: testModule(t, "issue-7824"),
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
// Write / Read plan to simulate running it through a Plan file
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
_, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
// GH-5254
|
|
func TestContext2Apply_issue5254(t *testing.T) {
|
|
// Create a provider. We use "template" here just to match the repro
|
|
// we got from the issue itself.
|
|
p := testProvider("template")
|
|
p.ResourcesReturn = append(p.ResourcesReturn, ResourceType{
|
|
Name: "template_file",
|
|
})
|
|
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
// Apply cleanly step 0
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: testModule(t, "issue-5254/step-0"),
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
// Application success. Now make the modification and store a plan
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: testModule(t, "issue-5254/step-1"),
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
plan, diags = ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
// Write / Read plan to simulate running it through a Plan file
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
template_file.child:
|
|
ID = foo
|
|
provider = provider.template
|
|
template = Hi
|
|
type = template_file
|
|
|
|
Dependencies:
|
|
template_file.parent.*
|
|
template_file.parent:
|
|
ID = foo
|
|
provider = provider.template
|
|
template = Hi
|
|
type = template_file
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_targetedWithTaintedInState(t *testing.T) {
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
p.ApplyFn = testApplyFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: testModule(t, "apply-tainted-targets"),
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Resource(
|
|
addrs.ManagedResourceMode, "aws_instance", "iambeingadded",
|
|
),
|
|
},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.ifailedprovisioners": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "ifailedprovisioners",
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
// Write / Read plan to simulate running it through a Plan file
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
Config: testModule(t, "apply-tainted-targets"),
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.iambeingadded:
|
|
ID = foo
|
|
provider = provider.aws
|
|
aws_instance.ifailedprovisioners: (tainted)
|
|
ID = ifailedprovisioners
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
// Higher level test exposing the bug this covers in
|
|
// TestResource_ignoreChangesRequired
|
|
func TestContext2Apply_ignoreChangesCreate(t *testing.T) {
|
|
m := testModule(t, "apply-ignore-changes-create")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"]
|
|
instanceSchema.Attributes["required_field"] = &configschema.Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("bad: %s", state)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
// Expect no changes from original state
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
required_field = set
|
|
type = aws_instance
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_ignoreChangesWithDep(t *testing.T) {
|
|
m := testModule(t, "apply-ignore-changes-dep")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
|
|
p.DiffFn = func(i *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) {
|
|
switch i.Type {
|
|
case "aws_instance":
|
|
newAmi, _ := c.Get("ami")
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"ami": &ResourceAttrDiff{
|
|
Old: s.Attributes["ami"],
|
|
New: newAmi.(string),
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
}, nil
|
|
case "aws_eip":
|
|
return testDiffFn(i, s, c)
|
|
default:
|
|
t.Fatalf("Unexpected type: %s", i.Type)
|
|
return nil, nil
|
|
}
|
|
}
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo.0": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "i-abc123",
|
|
Attributes: map[string]string{
|
|
"ami": "ami-abcd1234",
|
|
"id": "i-abc123",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.1": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "i-bcd234",
|
|
Attributes: map[string]string{
|
|
"ami": "ami-abcd1234",
|
|
"id": "i-bcd234",
|
|
},
|
|
},
|
|
},
|
|
"aws_eip.foo.0": &ResourceState{
|
|
Type: "aws_eip",
|
|
Primary: &InstanceState{
|
|
ID: "eip-abc123",
|
|
Attributes: map[string]string{
|
|
"id": "eip-abc123",
|
|
"instance": "i-abc123",
|
|
},
|
|
},
|
|
},
|
|
"aws_eip.foo.1": &ResourceState{
|
|
Type: "aws_eip",
|
|
Primary: &InstanceState{
|
|
ID: "eip-bcd234",
|
|
Attributes: map[string]string{
|
|
"id": "eip-bcd234",
|
|
"instance": "i-bcd234",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(s.String())
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_ignoreChangesWildcard(t *testing.T) {
|
|
m := testModule(t, "apply-ignore-changes-wildcard")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"]
|
|
instanceSchema.Attributes["required_field"] = &configschema.Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if p, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("bad: %s", state)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
// Expect no changes from original state
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
required_field = set
|
|
type = aws_instance
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
// https://github.com/hashicorp/terraform/issues/7378
|
|
func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-nested-module-with-attrs")
|
|
p := testProvider("null")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
var state *State
|
|
var diags tfdiags.Diagnostics
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Config: m,
|
|
State: state,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy plan err: %s", diags.Err())
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := WritePlan(plan, &buf); err != nil {
|
|
t.Fatalf("plan write err: %s", err)
|
|
}
|
|
|
|
planFromFile, err := ReadPlan(&buf)
|
|
if err != nil {
|
|
t.Fatalf("plan read err: %s", err)
|
|
}
|
|
|
|
ctx, diags = planFromFile.Context(&ContextOpts{
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("destroy apply err: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
//Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
module.middle:
|
|
<no state>
|
|
module.middle.bottom:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
// If a data source explicitly depends on another resource, it's because we need
|
|
// that resource to be applied first.
|
|
func TestContext2Apply_dataDependsOn(t *testing.T) {
|
|
p := testProvider("null")
|
|
m := testModule(t, "apply-data-depends-on")
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
// the "provisioner" here writes to this variable, because the intent is to
|
|
// create a dependency which can't be viewed through the graph, and depends
|
|
// solely on the configuration providing "depends_on"
|
|
provisionerOutput := ""
|
|
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
// the side effect of the resource being applied
|
|
provisionerOutput = "APPLIED"
|
|
return testApplyFn(info, s, d)
|
|
}
|
|
|
|
p.DiffFn = testDiffFn
|
|
p.ReadDataDiffFn = testDataDiffFn
|
|
|
|
p.ReadDataApplyFn = func(*InstanceInfo, *InstanceDiff) (*InstanceState, error) {
|
|
// Read the artifact created by our dependency being applied.
|
|
// Without any "depends_on", this would be skipped as it's assumed the
|
|
// initial diff during refresh was all that's needed.
|
|
return &InstanceState{
|
|
ID: "read",
|
|
Attributes: map[string]string{
|
|
"foo": provisionerOutput,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
_, diags := ctx.Refresh()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
t.Fatal("nil pointer below")
|
|
root := state.ModuleByPath(addrs.RootModuleInstance)
|
|
actual := root.Resources["data.null_data_source.read"].Primary.Attributes["foo"]
|
|
|
|
expected := "APPLIED"
|
|
if actual != expected {
|
|
t.Fatalf("bad:\n%s", strings.TrimSpace(state.String()))
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_terraformWorkspace(t *testing.T) {
|
|
m := testModule(t, "apply-terraform-workspace")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Meta: &ContextMeta{Env: "foo"},
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("plan errors: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
actual := state.RootModule().Outputs["output"]
|
|
expected := "foo"
|
|
if actual == nil || actual.Value != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// verify that multiple config references only create a single depends_on entry
|
|
func TestContext2Apply_multiRef(t *testing.T) {
|
|
m := testModule(t, "apply-multi-ref")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
deps := state.Modules[0].Resources["aws_instance.other"].Dependencies
|
|
if len(deps) > 1 || deps[0] != "aws_instance.create" {
|
|
t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_targetedModuleRecursive(t *testing.T) {
|
|
m := testModule(t, "apply-targeted-module-recursive")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
Targets: []addrs.Targetable{
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey),
|
|
},
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
mod := state.ModuleByPath(
|
|
addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("subchild", addrs.NoKey),
|
|
)
|
|
if mod == nil {
|
|
t.Fatalf("no subchild module found in the state!\n\n%#v", state)
|
|
}
|
|
if len(mod.Resources) != 1 {
|
|
t.Fatalf("expected 1 resources, got: %#v", mod.Resources)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child.subchild:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
provider = provider.aws
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_localVal(t *testing.T) {
|
|
m := testModule(t, "apply-local-val")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("error during plan: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("error during apply: %s", diags.Err())
|
|
}
|
|
|
|
got := strings.TrimSpace(state.String())
|
|
want := strings.TrimSpace(`
|
|
<no state>
|
|
Outputs:
|
|
|
|
result_1 = hello
|
|
result_3 = hello world
|
|
|
|
module.child:
|
|
<no state>
|
|
Outputs:
|
|
|
|
result = hello
|
|
`)
|
|
if got != want {
|
|
t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyWithLocals(t *testing.T) {
|
|
m := testModule(t, "apply-destroy-with-locals")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) {
|
|
d, err := testDiffFn(info, s, c)
|
|
return d, err
|
|
}
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Outputs: map[string]*OutputState{
|
|
"name": &OutputState{
|
|
Type: "string",
|
|
Value: "test-bar",
|
|
},
|
|
},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.foo": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
// FIXME: id should only exist in one place
|
|
Attributes: map[string]string{
|
|
"id": "foo",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("error during apply: %s", diags.Err())
|
|
}
|
|
|
|
got := strings.TrimSpace(state.String())
|
|
want := strings.TrimSpace(`<no state>`)
|
|
if got != want {
|
|
t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_providerWithLocals(t *testing.T) {
|
|
m := testModule(t, "provider-with-locals")
|
|
p := testProvider("aws")
|
|
|
|
providerRegion := ""
|
|
// this should not be overridden during destroy
|
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
|
if r, ok := c.Get("region"); ok {
|
|
providerRegion = r.(string)
|
|
}
|
|
return nil
|
|
}
|
|
p.DiffFn = testDiffFn
|
|
p.ApplyFn = testApplyFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
state, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("err: %s", diags.Err())
|
|
}
|
|
|
|
if state.HasResources() {
|
|
t.Fatal("expected no state, got:", state)
|
|
}
|
|
|
|
if providerRegion != "bar" {
|
|
t.Fatalf("expected region %q, got: %q", "bar", providerRegion)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_destroyWithProviders(t *testing.T) {
|
|
m := testModule(t, "destroy-module-with-provider")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{"root", "mod", "removed"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.child": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
// this provider doesn't exist
|
|
Provider: "provider.aws.baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: s,
|
|
Destroy: true,
|
|
})
|
|
|
|
// test that we can't destroy if the provider is missing
|
|
if _, diags := ctx.Plan(); diags == nil {
|
|
t.Fatal("expected plan error, provider.aws.baz doesn't exist")
|
|
}
|
|
|
|
// correct the state
|
|
s.Modules[2].Resources["aws_instance.child"].Provider = "provider.aws.bar"
|
|
|
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("error during apply: %s", diags.Err())
|
|
}
|
|
|
|
got := strings.TrimSpace(state.String())
|
|
|
|
want := strings.TrimSpace("<no state>")
|
|
if got != want {
|
|
t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_providersFromState(t *testing.T) {
|
|
m := configs.NewEmptyConfig()
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
state *State
|
|
output string
|
|
err bool
|
|
}{
|
|
{
|
|
name: "add implicit provider",
|
|
state: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: false,
|
|
output: "<no state>",
|
|
},
|
|
|
|
// an aliased provider must be in the config to remove a resource
|
|
{
|
|
name: "add aliased provider",
|
|
state: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Provider: "provider.aws.bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
|
|
// a provider in a module implies some sort of config, so this isn't
|
|
// allowed even without an alias
|
|
{
|
|
name: "add unaliased module provider",
|
|
state: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "bar",
|
|
},
|
|
Provider: "module.child.provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
),
|
|
State: tc.state,
|
|
})
|
|
|
|
_, diags := ctx.Plan()
|
|
if tc.err {
|
|
if diags == nil {
|
|
t.Fatal("expected error")
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
if !tc.err && diags.HasErrors() {
|
|
t.Fatal(diags.Err())
|
|
}
|
|
|
|
state, diags := ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("diags: %s", diags.Err())
|
|
}
|
|
|
|
checkStateString(t, state, "<no state>")
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_plannedInterpolatedCount(t *testing.T) {
|
|
m := testModule(t, "apply-interpolated-count")
|
|
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
providerResolver := ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
)
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.test": {
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Config: m,
|
|
ProviderResolver: providerResolver,
|
|
State: s,
|
|
})
|
|
|
|
plan, diags := ctx.Plan()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("plan failed: %s", diags.Err())
|
|
}
|
|
|
|
// We'll marshal and unmarshal the plan here, to ensure that we have
|
|
// a clean new context as would be created if we separately ran
|
|
// terraform plan -out=tfplan && terraform apply tfplan
|
|
var planBuf bytes.Buffer
|
|
err := WritePlan(plan, &planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to write plan: %s", err)
|
|
}
|
|
plan, err = ReadPlan(&planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to read plan: %s", err)
|
|
}
|
|
|
|
ctx, diags = plan.Context(&ContextOpts{
|
|
ProviderResolver: providerResolver,
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("failed to create context for plan: %s", diags.Err())
|
|
}
|
|
|
|
// Applying the plan should now succeed
|
|
_, diags = ctx.Apply()
|
|
if diags.HasErrors() {
|
|
t.Fatalf("apply failed: %s", diags.Err())
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) {
|
|
m := testModule(t, "plan-destroy-interpolated-count")
|
|
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
providerResolver := ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
)
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a.0": {
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
"aws_instance.a.1": {
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
Outputs: map[string]*OutputState{
|
|
"out": {
|
|
Type: "list",
|
|
Value: []string{"foo", "foo"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
ProviderResolver: providerResolver,
|
|
State: s,
|
|
Destroy: true,
|
|
})
|
|
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("plan failed: %s", err)
|
|
}
|
|
|
|
// We'll marshal and unmarshal the plan here, to ensure that we have
|
|
// a clean new context as would be created if we separately ran
|
|
// terraform plan -out=tfplan && terraform apply tfplan
|
|
var planBuf bytes.Buffer
|
|
err = WritePlan(plan, &planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to write plan: %s", err)
|
|
}
|
|
plan, err = ReadPlan(&planBuf)
|
|
if err != nil {
|
|
t.Fatalf("failed to read plan: %s", err)
|
|
}
|
|
|
|
ctx, err = plan.Context(&ContextOpts{
|
|
ProviderResolver: providerResolver,
|
|
Destroy: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create context for plan: %s", err)
|
|
}
|
|
|
|
// Applying the plan should now succeed
|
|
_, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply failed: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_scaleInMultivarRef(t *testing.T) {
|
|
m := testModule(t, "apply-resource-scale-in")
|
|
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
|
|
providerResolver := ResourceProviderResolverFixed(
|
|
map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
)
|
|
|
|
s := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.one": {
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
"aws_instance.two": {
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"val": "foo",
|
|
},
|
|
},
|
|
Provider: "provider.aws",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
ProviderResolver: providerResolver,
|
|
State: s,
|
|
Variables: map[string]interface{}{"count": "0"},
|
|
})
|
|
|
|
_, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("plan failed: %s", err)
|
|
}
|
|
|
|
// Applying the plan should now succeed
|
|
_, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply failed: %s", err)
|
|
}
|
|
}
|