mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-06 14:13:16 -06:00
0e4a6e3e89
Fixes #10440 This updates the behavior of "apply" resources to depend on the destroy versions of their dependencies. We make an exception to this behavior when the "apply" resource is CBD. This is odd and not 100% correct, but it mimics the behavior of the legacy graphs and avoids us having to do major core work to support the 100% correct solution. I'll explain this in examples... Given the following configuration: resource "null_resource" "a" { count = "${var.count}" } resource "null_resource" "b" { triggers { key = "${join(",", null_resource.a.*.id)}" } } Assume we've successfully created this configuration with count = 2. When going from count = 2 to count = 1, `null_resource.b` should wait for `null_resource.a.1` to destroy. If it doesn't, then it is a race: depending when we interpolate the `triggers.key` attribute of `null_resource.b`, we may get 1 value or 2. If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get 2. This was the root cause of #10440 In the legacy graphs, `null_resource.b` would depend on the destruction of any `null_resource.a` (orphans, tainted, anything!). This would ensure proper ordering. We mimic that behavior here. The difference is CBD. If `null_resource.b` has CBD enabled, then the ordering **in the legacy graph** becomes: 1. null_resource.b (create) 2. null_resource.b (destroy) 3. null_resource.a (destroy) In this case, the update would always have 2 values for `triggers.key`, even though we were destroying a resource later! This scenario required two `terraform apply` operations. This is what the CBD check is for in this PR. We do this to mimic the behavior of the legacy graph. The correct solution to do one day is to allow splat references (`null_resource.a.*.id`) to happen in parallel and only read up to to the `count` amount in the state. This requires some fairly significant work close to the 0.8 release date, so we can defer this to later and adopt the 0.7.x behavior for now.
6905 lines
149 KiB
Go
6905 lines
149 KiB
Go
package terraform
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform/config/module"
|
|
)
|
|
|
|
func TestContext2Apply_basic(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_escape(t *testing.T) {
|
|
m := testModule(t, "apply-escape")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = foo
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`null_resource.foo:
|
|
ID = foo
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
|
|
{
|
|
// 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.aws_instance.child" {
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should check")
|
|
}
|
|
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
{
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("should check")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
<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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: globalState,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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.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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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.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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"another": testProviderFuncFixed(p2),
|
|
},
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", 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, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.foo:
|
|
ID = foo
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected)
|
|
}
|
|
|
|
if !p.ConfigureCalled {
|
|
t.Fatalf("provider Configure() was never called!")
|
|
}
|
|
}
|
|
|
|
// Higher level test at TestResource_dataSourceListApplyPanic
|
|
func TestContext2Apply_computedAttrRefTypeMismatch(t *testing.T) {
|
|
m := testModule(t, "apply-computed-attr-ref-type-mismatch")
|
|
p := testProvider("aws")
|
|
p.DiffFn = testDiffFn
|
|
p.ValidateResourceFn = func(t string, c *ResourceConfig) (ws []string, es []error) {
|
|
// Emulate the type checking behavior of helper/schema based validation
|
|
if t == "aws_instance" {
|
|
ami, _ := c.Get("ami")
|
|
switch a := ami.(type) {
|
|
case string:
|
|
// ok
|
|
default:
|
|
es = append(es, fmt.Errorf("Expected ami to be string, got %T", a))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
p.DiffFn = func(
|
|
info *InstanceInfo,
|
|
state *InstanceState,
|
|
c *ResourceConfig) (*InstanceDiff, error) {
|
|
switch info.Type {
|
|
case "aws_ami_list":
|
|
// Emulate a diff that says "we'll create this list and ids will be populated"
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"ids.#": &ResourceAttrDiff{NewComputed: true},
|
|
},
|
|
}, nil
|
|
case "aws_instance":
|
|
// If we get to the diff for instance, we should be able to assume types
|
|
ami, _ := c.Get("ami")
|
|
_ = ami.(string)
|
|
}
|
|
return nil, nil
|
|
}
|
|
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
|
|
if info.Type != "aws_ami_list" {
|
|
t.Fatalf("Reached apply for unexpected resource type! %s", info.Type)
|
|
}
|
|
// Pretend like we make a thing and the computed list "ids" is populated
|
|
return &InstanceState{
|
|
ID: "someid",
|
|
Attributes: map[string]string{
|
|
"ids.#": "2",
|
|
"ids.0": "ami-abc123",
|
|
"ids.1": "ami-bcd345",
|
|
},
|
|
}, nil
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
_, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatalf("Expected err, got none!")
|
|
}
|
|
|
|
expected := "Expected ami to be string"
|
|
if !strings.Contains(err.Error(), expected) {
|
|
t.Fatalf("expected:\n\n%s\n\nto contain:\n\n%s", err, expected)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = foo
|
|
require_new = yes
|
|
type = aws_instance
|
|
value = foo
|
|
aws_instance.foo:
|
|
ID = foo
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar.0:
|
|
ID = foo
|
|
foo = bar
|
|
type = aws_instance
|
|
aws_instance.bar.1:
|
|
ID = foo
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = bar
|
|
`)
|
|
}
|
|
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
Parallelism: 1, // To check ordering
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
Parallelism: 1, // To check ordering
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDataBasicStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "-",
|
|
Attributes: map[string]string{
|
|
"inputs.#": "1",
|
|
"inputs.test": "yes",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
newState, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
_, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
|
|
p_tf := testProvider("terraform")
|
|
p_tf.ApplyFn = testApplyFn
|
|
p_tf.DiffFn = testDiffFn
|
|
|
|
providers := map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p_aws),
|
|
"terraform": testProviderFuncFixed(p_tf),
|
|
}
|
|
|
|
// Bug only appears from time to time,
|
|
// so we run this test multiple times
|
|
// to check for the race-condition
|
|
for i := 0; i <= 10; i++ {
|
|
ctx := getContextForApply_destroyCrossProviders(
|
|
t, m, providers)
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func getContextForApply_destroyCrossProviders(
|
|
t *testing.T,
|
|
m *module.Tree,
|
|
providers map[string]ResourceProviderFactory) *Context {
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"terraform_remote_state.shared": &ResourceState{
|
|
Type: "terraform_remote_state",
|
|
Primary: &InstanceState{
|
|
ID: "remote-2652591293",
|
|
Attributes: map[string]string{
|
|
"output.env_name": "test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyMinimalStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_badDiff(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"newp": nil,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err == 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{
|
|
Module: m,
|
|
Providers: 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
|
|
}
|
|
}
|
|
}
|
|
|
|
return &InstanceState{
|
|
ID: "foo",
|
|
Attributes: map[string]string{
|
|
"num": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"num": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Start the Apply in a goroutine
|
|
var applyErr error
|
|
stateCh := make(chan *State)
|
|
go func() {
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
applyErr = err
|
|
}
|
|
|
|
stateCh <- state
|
|
}()
|
|
|
|
state := <-stateCh
|
|
if applyErr != nil {
|
|
t.Fatalf("err: %s", applyErr)
|
|
}
|
|
|
|
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("bad: \n%s", actual)
|
|
}
|
|
|
|
if !p.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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ctx.variables = map[string]interface{}{"value": "1"}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyComputeStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
testStringMatch(t, p, testTerraformApplyCountDecToOneCorruptedPlanStr)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountTaintedStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountVariableStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_mapVariableOverride(t *testing.T) {
|
|
m := testModule(t, "apply-map-var-override")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"images": []map[string]interface{}{
|
|
map[string]interface{}{
|
|
"us-west-2": "overridden",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.bar:
|
|
ID = foo
|
|
ami = overridden
|
|
type = aws_instance
|
|
aws_instance.foo:
|
|
ID = foo
|
|
ami = image-1234
|
|
type = aws_instance
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("got: \n%s\nexpected: \n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_moduleBasic(t *testing.T) {
|
|
m := testModule(t, "apply-module")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "b",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
&ModuleState{
|
|
Path: []string{"root", "child"},
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.a": &ResourceState{
|
|
Type: "aws_instance",
|
|
Primary: &InstanceState{
|
|
ID: "a",
|
|
},
|
|
},
|
|
},
|
|
Outputs: map[string]*OutputState{
|
|
"a_output": &OutputState{
|
|
Type: "string",
|
|
Sensitive: false,
|
|
Value: "a",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("child"); !ok {
|
|
return nil
|
|
}
|
|
|
|
if _, ok := c.Get("root"); ok {
|
|
return fmt.Errorf("child should not get root")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
<no state>
|
|
module.child:
|
|
aws_instance.foo:
|
|
ID = foo
|
|
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{
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !called {
|
|
t.Fatal("must call configure")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
module.child:
|
|
<no 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{
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"test": testProviderFuncFixed(pTest),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"no.thing"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "2",
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "5",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyModuleBoolStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
"do": testProviderFuncFixed(pDO),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "3",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "1",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "2",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
t.Logf("Step 1 state: %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,
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"count": "1",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !checked {
|
|
t.Fatal("apply never called")
|
|
}
|
|
|
|
t.Logf("Step 2 state: %s", state)
|
|
|
|
s = state
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_nilDiff(t *testing.T) {
|
|
m := testModule(t, "apply-good")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err == 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil || !strings.Contains(err.Error(), "abcd") {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `<no state>`)
|
|
}
|
|
|
|
{
|
|
// Create the standard apply function and verify we get the output
|
|
p.ApplyFn = testApplyFn
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !called {
|
|
t.Fatal("configure never called")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_provisionerModule(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-module")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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["foo"]
|
|
if !ok || val != "computed_dynamical" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"value": "1",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"value": "1",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", 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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == 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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == 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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Destroy is broken, so even though CBD successfully replaces the instance,
|
|
// we'll have to save the Deposed instance to destroy later
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.web: (1 deposed)
|
|
ID = bar
|
|
Deposed ID 1 = foo
|
|
`)
|
|
|
|
createdInstanceId = "baz"
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: ps,
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// We're replacing the primary instance once again. Destroy is _still_
|
|
// broken, so the Deposed list gets longer
|
|
state, err = ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.web: (2 deposed)
|
|
ID = baz
|
|
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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
state, err = ctx.Apply()
|
|
// Expect error because 1/2 of Deposed destroys failed
|
|
if err == 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
|
|
Deposed ID 1 = bar
|
|
`)
|
|
|
|
// Destroy working fully!
|
|
destroyFunc = func(is *InstanceState) (*InstanceState, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
createdInstanceId = "quux"
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatal("should not have error:", err)
|
|
}
|
|
|
|
// And finally the state is clean
|
|
checkStateString(t, state, `
|
|
aws_instance.web:
|
|
ID = quux
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_provisionerResourceRef(t *testing.T) {
|
|
m := testModule(t, "apply-provisioner-resource-ref")
|
|
p := testProvider("aws")
|
|
pr := testProvisioner()
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
|
val, ok := c.Config["foo"]
|
|
if !ok || val != "2" {
|
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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_provisionerMultiSelfRefCount(t *testing.T) {
|
|
var lock sync.Mutex
|
|
commands := make([]string, 0, 5)
|
|
|
|
m := testModule(t, "apply-provisioner-multi-self-ref-count")
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
|
|
// Verify our result
|
|
sort.Strings(commands)
|
|
expectedCommands := []string{"3", "3", "3"}
|
|
if !reflect.DeepEqual(commands, expectedCommands) {
|
|
t.Fatalf("bad: %#v", commands)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
_, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Verify apply was invoked
|
|
if !pr.ApplyCalled {
|
|
t.Fatalf("provisioner not invoked")
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Destroy: true,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
_, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state2, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual = strings.TrimSpace(state2.String())
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Providers: 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"
|
|
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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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.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["pass"] != "test" {
|
|
t.Fatalf("Bad: %#v", conn)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"value": "1",
|
|
"pass": "test",
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyProvisionerStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Next, plan and apply a destroy operation
|
|
h.Active = true
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
t.Logf("State 1: %s", state)
|
|
|
|
// Next, plan and apply config-less to force a destroy with "apply"
|
|
h.Active = true
|
|
ctx = testContext2(t, &ContextOpts{
|
|
State: state,
|
|
Module: module.NewEmptyTree(),
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDestroyStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
|
|
// 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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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,
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyDestroyNestedModuleStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Test that things were destroyed
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
module.child.subchild.subsubchild:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%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
|
|
var err error
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
} else {
|
|
t.Logf("Step 1 plan: %s", p)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply err: %s", err)
|
|
}
|
|
|
|
t.Logf("Step 1 state: %s", state)
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Module: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"key_name": "foobarkey",
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("destroy plan err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("destroy apply err: %s", 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 err error
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply err: %s", err)
|
|
}
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Module: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("destroy plan err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
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_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 err error
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply err: %s", err)
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"module.child"},
|
|
})
|
|
|
|
_, 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 err error
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply err: %s", err)
|
|
}
|
|
}
|
|
|
|
h := new(HookRecordApplyOrder)
|
|
h.Active = true
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Module: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("destroy plan err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
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>
|
|
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")
|
|
h := new(HookRecordApplyOrder)
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Next, plan and apply a destroy operation
|
|
h.Active = true
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
State: state,
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
mod := state.RootModule()
|
|
if len(mod.Resources) > 0 {
|
|
t.Fatalf("bad: %#v", mod)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: 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{
|
|
"num": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Provisioners: map[string]ResourceProvisionerFactory{
|
|
"shell": testProvisionerFuncFixed(pr),
|
|
},
|
|
State: s,
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_error(t *testing.T) {
|
|
errored := false
|
|
|
|
m := testModule(t, "apply-error")
|
|
p := testProvider("aws")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: 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{
|
|
"num": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"num": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should have error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyErrorStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", 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{
|
|
Module: m,
|
|
Providers: 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{
|
|
"num": "2",
|
|
},
|
|
}, nil
|
|
}
|
|
p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) {
|
|
return &InstanceDiff{
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
"num": &ResourceAttrDiff{
|
|
New: "bar",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == 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("bad: \n%s", 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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
State: state,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if _, err := ctx.Apply(); err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: 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 _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
_, err := ctx.Plan()
|
|
if err == nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "is not a valid type") {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m1,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p1),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx1.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state1, err := ctx1.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
m2 := testModule(t, "apply-output-add-after")
|
|
p2 := testProvider("aws")
|
|
p2.ApplyFn = testApplyFn
|
|
p2.DiffFn = testDiffFn
|
|
ctx2 := testContext2(t, &ContextOpts{
|
|
Module: m2,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p2),
|
|
},
|
|
State: state1,
|
|
})
|
|
|
|
if _, err := ctx2.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state2, err := ctx2.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state2.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputAddStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputMultiStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"aws_instance.foo"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
num = 2
|
|
type = aws_instance
|
|
`)
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"aws_instance.foo"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo.0:
|
|
ID = foo
|
|
aws_instance.foo.1:
|
|
ID = foo
|
|
aws_instance.foo.2:
|
|
ID = foo
|
|
`)
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"aws_instance.foo[1]"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo.1:
|
|
ID = foo
|
|
`)
|
|
}
|
|
|
|
func TestContext2Apply_targetedDestroy(t *testing.T) {
|
|
m := testModule(t, "apply-targeted")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: 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: []string{"aws_instance.foo"},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
`)
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: 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: []string{"aws_instance.foo"},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Providers: 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: []string{"module.child.aws_instance.foo"},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar:
|
|
ID = i-abc123
|
|
aws_instance.foo:
|
|
ID = i-bcd345
|
|
|
|
module.child:
|
|
aws_instance.bar:
|
|
ID = i-abc123
|
|
`)
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: 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: []string{
|
|
"aws_instance.foo[2]",
|
|
"aws_instance.bar[1]",
|
|
},
|
|
Destroy: true,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.bar.0:
|
|
ID = i-abc123
|
|
aws_instance.bar.2:
|
|
ID = i-abc123
|
|
aws_instance.foo.0:
|
|
ID = i-bcd345
|
|
aws_instance.foo.1:
|
|
ID = i-bcd345
|
|
`)
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"module.child"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
mod := state.ModuleByPath([]string{"root", "child"})
|
|
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
|
|
num = 2
|
|
type = aws_instance
|
|
aws_instance.foo:
|
|
ID = foo
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"aws_instance.foo"},
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf("Diff: %s", p)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
checkStateString(t, state, `
|
|
aws_instance.foo:
|
|
ID = foo
|
|
foo = foo
|
|
type = aws_instance
|
|
|
|
Dependencies:
|
|
module.child
|
|
|
|
module.child:
|
|
aws_instance.mod:
|
|
ID = foo
|
|
|
|
Outputs:
|
|
|
|
output = 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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"module.child.aws_instance.foo"},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
mod := state.ModuleByPath([]string{"root", "child"})
|
|
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
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err == nil {
|
|
t.Fatal("should error")
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_vars(t *testing.T) {
|
|
m := testModule(t, "apply-vars")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Variables: map[string]interface{}{
|
|
"foo": "us-west-2",
|
|
"test_list": []interface{}{"Hello", "World"},
|
|
"test_map": map[string]interface{}{
|
|
"Hello": "World",
|
|
"Foo": "Bar",
|
|
"Baz": "Foo",
|
|
},
|
|
"amis": []map[string]interface{}{
|
|
map[string]interface{}{
|
|
"us-east-1": "override",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
w, e := ctx.Validate()
|
|
if len(w) > 0 {
|
|
t.Fatalf("bad: %#v", w)
|
|
}
|
|
if len(e) > 0 {
|
|
t.Fatalf("bad: %s", e)
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyVarsStr)
|
|
if actual != expected {
|
|
t.Fatalf("expected: %s\n got:\n%s", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_varsEnv(t *testing.T) {
|
|
// Set the env var
|
|
defer tempEnv(t, "TF_VAR_ami", "baz")()
|
|
defer tempEnv(t, "TF_VAR_list", `["Hello", "World"]`)()
|
|
defer tempEnv(t, "TF_VAR_map", `{"Hello" = "World", "Foo" = "Bar", "Baz" = "Foo"}`)()
|
|
|
|
m := testModule(t, "apply-vars-env")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
w, e := ctx.Validate()
|
|
if len(w) > 0 {
|
|
t.Fatalf("bad: %#v", w)
|
|
}
|
|
if len(e) > 0 {
|
|
t.Fatalf("bad: %s", e)
|
|
}
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(testTerraformApplyVarsEnvStr)
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf("plan: %s", p)
|
|
}
|
|
|
|
h.Active = true
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: m,
|
|
Hooks: []Hook{h},
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: state,
|
|
})
|
|
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
h.Active = true
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: testModule(t, "issue-7824"),
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
_, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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{
|
|
Module: testModule(t, "issue-5254/step-0"),
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Application success. Now make the modification and store a plan
|
|
ctx = testContext2(t, &ContextOpts{
|
|
Module: testModule(t, "issue-5254/step-1"),
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
plan, err = ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"template": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
template_file.child:
|
|
ID = foo
|
|
template = Hi
|
|
type = template_file
|
|
|
|
Dependencies:
|
|
template_file.parent
|
|
template_file.parent:
|
|
ID = foo
|
|
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{
|
|
Module: testModule(t, "apply-tainted-targets"),
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
Targets: []string{"aws_instance.iambeingadded"},
|
|
State: &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_instance.ifailedprovisioners": &ResourceState{
|
|
Primary: &InstanceState{
|
|
ID: "ifailedprovisioners",
|
|
Tainted: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Module: testModule(t, "apply-tainted-targets"),
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(`
|
|
aws_instance.iambeingadded:
|
|
ID = foo
|
|
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
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
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{
|
|
Primary: &InstanceState{
|
|
ID: "i-abc123",
|
|
Attributes: map[string]string{
|
|
"ami": "ami-abcd1234",
|
|
"id": "i-abc123",
|
|
},
|
|
},
|
|
},
|
|
"aws_instance.foo.1": &ResourceState{
|
|
Primary: &InstanceState{
|
|
ID: "i-bcd234",
|
|
Attributes: map[string]string{
|
|
"ami": "ami-abcd1234",
|
|
"id": "i-bcd234",
|
|
},
|
|
},
|
|
},
|
|
"aws_eip.foo.0": &ResourceState{
|
|
Primary: &InstanceState{
|
|
ID: "eip-abc123",
|
|
Attributes: map[string]string{
|
|
"id": "eip-abc123",
|
|
"instance": "i-abc123",
|
|
},
|
|
},
|
|
},
|
|
"aws_eip.foo.1": &ResourceState{
|
|
Primary: &InstanceState{
|
|
ID: "eip-bcd234",
|
|
Attributes: map[string]string{
|
|
"id": "eip-bcd234",
|
|
"instance": "i-bcd234",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
State: s,
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := strings.TrimSpace(state.String())
|
|
expected := strings.TrimSpace(s.String())
|
|
if actual != expected {
|
|
t.Fatalf("bad: \n%s", actual)
|
|
}
|
|
}
|
|
|
|
func TestContext2Apply_ignoreChangesWildcard(t *testing.T) {
|
|
m := testModule(t, "apply-ignore-changes-wildcard")
|
|
p := testProvider("aws")
|
|
p.ApplyFn = testApplyFn
|
|
p.DiffFn = testDiffFn
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"aws": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
if p, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
} else {
|
|
t.Logf(p.String())
|
|
}
|
|
|
|
state, err := ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", 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
|
|
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 err error
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Module: m,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
// First plan and apply a create operation
|
|
if _, err := ctx.Plan(); err != nil {
|
|
t.Fatalf("plan err: %s", err)
|
|
}
|
|
|
|
state, err = ctx.Apply()
|
|
if err != nil {
|
|
t.Fatalf("apply err: %s", err)
|
|
}
|
|
}
|
|
|
|
{
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Destroy: true,
|
|
Module: m,
|
|
State: state,
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
plan, err := ctx.Plan()
|
|
if err != nil {
|
|
t.Fatalf("destroy plan err: %s", 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, err = planFromFile.Context(&ContextOpts{
|
|
Providers: map[string]ResourceProviderFactory{
|
|
"null": testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
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.middle:
|
|
<no state>
|
|
module.middle.bottom:
|
|
<no state>
|
|
`)
|
|
if actual != expected {
|
|
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
|
|
}
|
|
}
|