Add tests and fix last issues
This commit is contained in:
parent
8560f50cbc
commit
d97b24e3c1
|
@ -1361,16 +1361,20 @@ const applyVarFileJSON = `
|
||||||
|
|
||||||
const testApplyDisableBackupStr = `
|
const testApplyDisableBackupStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
const testApplyDisableBackupStateStr = `
|
const testApplyDisableBackupStateStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
const testApplyStateStr = `
|
const testApplyStateStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
const testApplyStateDiffStr = `
|
const testApplyStateDiffStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
|
@ -603,8 +603,10 @@ const testPlanNoStateStr = `
|
||||||
|
|
||||||
const testPlanStateStr = `
|
const testPlanStateStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
||||||
const testPlanStateDefaultStr = `
|
const testPlanStateDefaultStr = `
|
||||||
ID = bar
|
ID = bar
|
||||||
|
Tainted = false
|
||||||
`
|
`
|
||||||
|
|
|
@ -347,9 +347,8 @@ func TestTaint_module(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testTaintStr = `
|
const testTaintStr = `
|
||||||
test_instance.foo: (1 tainted)
|
test_instance.foo: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTaintDefaultStr = `
|
const testTaintDefaultStr = `
|
||||||
|
@ -362,7 +361,6 @@ test_instance.foo:
|
||||||
ID = bar
|
ID = bar
|
||||||
|
|
||||||
module.child:
|
module.child:
|
||||||
test_instance.blah: (1 tainted)
|
test_instance.blah: (tainted)
|
||||||
ID = <not created>
|
ID = blah
|
||||||
Tainted ID 1 = blah
|
|
||||||
`
|
`
|
||||||
|
|
|
@ -17,8 +17,9 @@ func TestUntaint(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -49,101 +50,6 @@ test_instance.foo:
|
||||||
testStateOutput(t, statePath, expected)
|
testStateOutput(t, statePath, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUntaint_indexRequired(t *testing.T) {
|
|
||||||
state := &terraform.State{
|
|
||||||
Modules: []*terraform.ModuleState{
|
|
||||||
&terraform.ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*terraform.ResourceState{
|
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Tainted: []*terraform.InstanceState{
|
|
||||||
&terraform.InstanceState{ID: "bar"},
|
|
||||||
&terraform.InstanceState{ID: "bar2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
statePath := testStateFile(t, state)
|
|
||||||
|
|
||||||
ui := new(cli.MockUi)
|
|
||||||
c := &UntaintCommand{
|
|
||||||
Meta: Meta{
|
|
||||||
Ui: ui,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{
|
|
||||||
"-state", statePath,
|
|
||||||
"test_instance.foo",
|
|
||||||
}
|
|
||||||
if code := c.Run(args); code == 0 {
|
|
||||||
t.Fatalf("Expected non-zero exit. Output:\n\n%s", ui.OutputWriter.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing should have gotten untainted
|
|
||||||
expected := strings.TrimSpace(`
|
|
||||||
test_instance.foo: (2 tainted)
|
|
||||||
ID = <not created>
|
|
||||||
Tainted ID 1 = bar
|
|
||||||
Tainted ID 2 = bar2
|
|
||||||
`)
|
|
||||||
testStateOutput(t, statePath, expected)
|
|
||||||
|
|
||||||
// Should have gotten an error message mentioning index
|
|
||||||
errOut := ui.ErrorWriter.String()
|
|
||||||
errContains := "please specify an index"
|
|
||||||
if !strings.Contains(errOut, errContains) {
|
|
||||||
t.Fatalf("Expected err output: %s, to contain: %s", errOut, errContains)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUntaint_indexSpecified(t *testing.T) {
|
|
||||||
state := &terraform.State{
|
|
||||||
Modules: []*terraform.ModuleState{
|
|
||||||
&terraform.ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*terraform.ResourceState{
|
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Tainted: []*terraform.InstanceState{
|
|
||||||
&terraform.InstanceState{ID: "bar"},
|
|
||||||
&terraform.InstanceState{ID: "bar2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
statePath := testStateFile(t, state)
|
|
||||||
|
|
||||||
ui := new(cli.MockUi)
|
|
||||||
c := &UntaintCommand{
|
|
||||||
Meta: Meta{
|
|
||||||
Ui: ui,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{
|
|
||||||
"-state", statePath,
|
|
||||||
"-index", "1",
|
|
||||||
"test_instance.foo",
|
|
||||||
}
|
|
||||||
if code := c.Run(args); code != 0 {
|
|
||||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing should have gotten untainted
|
|
||||||
expected := strings.TrimSpace(`
|
|
||||||
test_instance.foo: (1 tainted)
|
|
||||||
ID = bar2
|
|
||||||
Tainted ID 1 = bar
|
|
||||||
`)
|
|
||||||
testStateOutput(t, statePath, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUntaint_backup(t *testing.T) {
|
func TestUntaint_backup(t *testing.T) {
|
||||||
// Get a temp cwd
|
// Get a temp cwd
|
||||||
tmp, cwd := testCwd(t)
|
tmp, cwd := testCwd(t)
|
||||||
|
@ -157,8 +63,9 @@ func TestUntaint_backup(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -183,9 +90,8 @@ func TestUntaint_backup(t *testing.T) {
|
||||||
|
|
||||||
// Backup is still tainted
|
// Backup is still tainted
|
||||||
testStateOutput(t, path+".backup", strings.TrimSpace(`
|
testStateOutput(t, path+".backup", strings.TrimSpace(`
|
||||||
test_instance.foo: (1 tainted)
|
test_instance.foo: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
`))
|
`))
|
||||||
|
|
||||||
// State is untainted
|
// State is untainted
|
||||||
|
@ -208,8 +114,9 @@ func TestUntaint_backupDisable(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -273,8 +180,9 @@ func TestUntaint_defaultState(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -311,8 +219,9 @@ func TestUntaint_missing(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -345,8 +254,9 @@ func TestUntaint_missingAllow(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -385,8 +295,9 @@ func TestUntaint_stateOut(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -411,9 +322,8 @@ func TestUntaint_stateOut(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testStateOutput(t, path, strings.TrimSpace(`
|
testStateOutput(t, path, strings.TrimSpace(`
|
||||||
test_instance.foo: (1 tainted)
|
test_instance.foo: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
`))
|
`))
|
||||||
testStateOutput(t, "foo", strings.TrimSpace(`
|
testStateOutput(t, "foo", strings.TrimSpace(`
|
||||||
test_instance.foo:
|
test_instance.foo:
|
||||||
|
@ -429,8 +339,9 @@ func TestUntaint_module(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.foo": &terraform.ResourceState{
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -440,8 +351,9 @@ func TestUntaint_module(t *testing.T) {
|
||||||
Resources: map[string]*terraform.ResourceState{
|
Resources: map[string]*terraform.ResourceState{
|
||||||
"test_instance.blah": &terraform.ResourceState{
|
"test_instance.blah": &terraform.ResourceState{
|
||||||
Type: "test_instance",
|
Type: "test_instance",
|
||||||
Tainted: []*terraform.InstanceState{
|
Primary: &terraform.InstanceState{
|
||||||
&terraform.InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -467,9 +379,8 @@ func TestUntaint_module(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testStateOutput(t, statePath, strings.TrimSpace(`
|
testStateOutput(t, statePath, strings.TrimSpace(`
|
||||||
test_instance.foo: (1 tainted)
|
test_instance.foo: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
|
|
||||||
module.child:
|
module.child:
|
||||||
test_instance.blah:
|
test_instance.blah:
|
||||||
|
|
|
@ -884,14 +884,13 @@ func TestContext2Apply_countTainted(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo.0": &ResourceState{
|
"aws_instance.foo.0": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"foo": "foo",
|
||||||
"foo": "foo",
|
"type": "aws_instance",
|
||||||
"type": "aws_instance",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2989,13 +2988,12 @@ func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo": &ResourceState{
|
"aws_instance.foo": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"id": "bar",
|
||||||
"id": "bar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3505,14 +3503,13 @@ func TestContext2Apply_taint(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "baz",
|
||||||
ID: "baz",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
"type": "aws_instance",
|
||||||
"type": "aws_instance",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3559,14 +3556,13 @@ func TestContext2Apply_taintDep(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo": &ResourceState{
|
"aws_instance.foo": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "baz",
|
||||||
ID: "baz",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
"type": "aws_instance",
|
||||||
"type": "aws_instance",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
|
@ -3622,14 +3618,13 @@ func TestContext2Apply_taintDepRequiresNew(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo": &ResourceState{
|
"aws_instance.foo": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "baz",
|
||||||
ID: "baz",
|
Attributes: map[string]string{
|
||||||
Attributes: map[string]string{
|
"num": "2",
|
||||||
"num": "2",
|
"type": "aws_instance",
|
||||||
"type": "aws_instance",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
|
@ -4438,10 +4433,9 @@ func TestContext2Apply_targetedWithTaintedInState(t *testing.T) {
|
||||||
Path: rootModulePath,
|
Path: rootModulePath,
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.ifailedprovisioners": &ResourceState{
|
"aws_instance.ifailedprovisioners": &ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "ifailedprovisioners",
|
||||||
ID: "ifailedprovisioners",
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4485,9 +4479,8 @@ func TestContext2Apply_targetedWithTaintedInState(t *testing.T) {
|
||||||
expected := strings.TrimSpace(`
|
expected := strings.TrimSpace(`
|
||||||
aws_instance.iambeingadded:
|
aws_instance.iambeingadded:
|
||||||
ID = foo
|
ID = foo
|
||||||
aws_instance.ifailedprovisioners: (1 tainted)
|
aws_instance.ifailedprovisioners: (tainted)
|
||||||
ID = <not created>
|
ID = ifailedprovisioners
|
||||||
Tainted ID 1 = ifailedprovisioners
|
|
||||||
`)
|
`)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
||||||
|
|
|
@ -1718,10 +1718,9 @@ func TestContext2Plan_taint(t *testing.T) {
|
||||||
},
|
},
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "baz",
|
||||||
ID: "baz",
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1748,57 +1747,6 @@ func TestContext2Plan_taint(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext2Plan_multiple_taint(t *testing.T) {
|
|
||||||
m := testModule(t, "plan-taint")
|
|
||||||
p := testProvider("aws")
|
|
||||||
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{"num": "2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"aws_instance.bar": &ResourceState{
|
|
||||||
Type: "aws_instance",
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{
|
|
||||||
ID: "baz",
|
|
||||||
},
|
|
||||||
&InstanceState{
|
|
||||||
ID: "zip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
|
||||||
Module: m,
|
|
||||||
Providers: map[string]ResourceProviderFactory{
|
|
||||||
"aws": testProviderFuncFixed(p),
|
|
||||||
},
|
|
||||||
State: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
plan, err := ctx.Plan()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := strings.TrimSpace(plan.String())
|
|
||||||
expected := strings.TrimSpace(testTerraformPlanMultipleTaintStr)
|
|
||||||
if actual != expected {
|
|
||||||
t.Fatalf("bad:\n%s", actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fails about 50% of the time before the fix for GH-4982, covers the fix.
|
// Fails about 50% of the time before the fix for GH-4982, covers the fix.
|
||||||
func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) {
|
func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) {
|
||||||
m := testModule(t, "plan-taint-interpolated-count")
|
m := testModule(t, "plan-taint-interpolated-count")
|
||||||
|
@ -1811,8 +1759,9 @@ func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo.0": &ResourceState{
|
"aws_instance.foo.0": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "bar"},
|
ID: "bar",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"aws_instance.foo.1": &ResourceState{
|
"aws_instance.foo.1": &ResourceState{
|
||||||
|
@ -1849,9 +1798,8 @@ DESTROY/CREATE: aws_instance.foo.0
|
||||||
|
|
||||||
STATE:
|
STATE:
|
||||||
|
|
||||||
aws_instance.foo.0: (1 tainted)
|
aws_instance.foo.0: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
aws_instance.foo.1:
|
aws_instance.foo.1:
|
||||||
ID = bar
|
ID = bar
|
||||||
aws_instance.foo.2:
|
aws_instance.foo.2:
|
||||||
|
|
|
@ -324,10 +324,9 @@ func TestContext2Refresh_modules(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.web": &ResourceState{
|
"aws_instance.web": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -650,10 +649,9 @@ func TestContext2Refresh_tainted(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.web": &ResourceState{
|
"aws_instance.web": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -670,7 +668,8 @@ func TestContext2Refresh_tainted(t *testing.T) {
|
||||||
|
|
||||||
p.RefreshFn = nil
|
p.RefreshFn = nil
|
||||||
p.RefreshReturn = &InstanceState{
|
p.RefreshReturn = &InstanceState{
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := ctx.Refresh()
|
s, err := ctx.Refresh()
|
||||||
|
|
|
@ -107,9 +107,13 @@ func testDiffFn(
|
||||||
info *InstanceInfo,
|
info *InstanceInfo,
|
||||||
s *InstanceState,
|
s *InstanceState,
|
||||||
c *ResourceConfig) (*InstanceDiff, error) {
|
c *ResourceConfig) (*InstanceDiff, error) {
|
||||||
var diff InstanceDiff
|
diff := new(InstanceDiff)
|
||||||
diff.Attributes = make(map[string]*ResourceAttrDiff)
|
diff.Attributes = make(map[string]*ResourceAttrDiff)
|
||||||
|
|
||||||
|
if s != nil {
|
||||||
|
diff.DestroyTainted = s.Tainted
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range c.Raw {
|
for k, v := range c.Raw {
|
||||||
if _, ok := v.(string); !ok {
|
if _, ok := v.(string); !ok {
|
||||||
continue
|
continue
|
||||||
|
@ -181,17 +185,21 @@ func testDiffFn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range diff.Attributes {
|
// If we recreate this resource because it's tainted, we keep all attrs
|
||||||
if v.NewComputed {
|
if !diff.RequiresNew() {
|
||||||
continue
|
for k, v := range diff.Attributes {
|
||||||
}
|
if v.NewComputed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
old, ok := s.Attributes[k]
|
old, ok := s.Attributes[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if old == v.New {
|
|
||||||
delete(diff.Attributes, k)
|
if old == v.New {
|
||||||
|
delete(diff.Attributes, k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +210,7 @@ func testDiffFn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &diff, nil
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testProvider(prefix string) *MockResourceProvider {
|
func testProvider(prefix string) *MockResourceProvider {
|
||||||
|
@ -276,9 +284,8 @@ root
|
||||||
`
|
`
|
||||||
|
|
||||||
const testContextRefreshModuleStr = `
|
const testContextRefreshModuleStr = `
|
||||||
aws_instance.web: (1 tainted)
|
aws_instance.web: (tainted)
|
||||||
ID = <not created>
|
ID = bar
|
||||||
Tainted ID 1 = bar
|
|
||||||
|
|
||||||
module.child:
|
module.child:
|
||||||
aws_instance.web:
|
aws_instance.web:
|
||||||
|
@ -300,7 +307,6 @@ const testContextRefreshOutputPartialStr = `
|
||||||
`
|
`
|
||||||
|
|
||||||
const testContextRefreshTaintedStr = `
|
const testContextRefreshTaintedStr = `
|
||||||
aws_instance.web: (1 tainted)
|
aws_instance.web: (tainted)
|
||||||
ID = <not created>
|
ID = foo
|
||||||
Tainted ID 1 = foo
|
|
||||||
`
|
`
|
||||||
|
|
|
@ -651,10 +651,9 @@ func TestContext2Validate_tainted(t *testing.T) {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.foo": &ResourceState{
|
"aws_instance.foo": &ResourceState{
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{
|
ID: "bar",
|
||||||
ID: "bar",
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -139,7 +139,6 @@ type EvalApplyProvisioners struct {
|
||||||
Resource *config.Resource
|
Resource *config.Resource
|
||||||
InterpResource *Resource
|
InterpResource *Resource
|
||||||
CreateNew *bool
|
CreateNew *bool
|
||||||
Tainted *bool
|
|
||||||
Error *error
|
Error *error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +158,7 @@ func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
|
|
||||||
if n.Error != nil && *n.Error != nil {
|
if n.Error != nil && *n.Error != nil {
|
||||||
// We're already errored creating, so mark as tainted and continue
|
// We're already errored creating, so mark as tainted and continue
|
||||||
if n.Tainted != nil {
|
state.Tainted = true
|
||||||
*n.Tainted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're already tainted, so just return out
|
// We're already tainted, so just return out
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -180,10 +177,10 @@ func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
// If there are no errors, then we append it to our output error
|
// If there are no errors, then we append it to our output error
|
||||||
// if we have one, otherwise we just output it.
|
// if we have one, otherwise we just output it.
|
||||||
err := n.apply(ctx)
|
err := n.apply(ctx)
|
||||||
if n.Tainted != nil {
|
|
||||||
*n.Tainted = err != nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Provisioning failed, so mark the resource as tainted
|
||||||
|
state.Tainted = true
|
||||||
|
|
||||||
if n.Error != nil {
|
if n.Error != nil {
|
||||||
*n.Error = multierror.Append(*n.Error, err)
|
*n.Error = multierror.Append(*n.Error, err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -69,8 +69,9 @@ type EvalDiff struct {
|
||||||
Info *InstanceInfo
|
Info *InstanceInfo
|
||||||
Config **ResourceConfig
|
Config **ResourceConfig
|
||||||
Provider *ResourceProvider
|
Provider *ResourceProvider
|
||||||
|
Diff **InstanceDiff
|
||||||
State **InstanceState
|
State **InstanceState
|
||||||
Output **InstanceDiff
|
OutputDiff **InstanceDiff
|
||||||
OutputState **InstanceState
|
OutputState **InstanceState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +105,11 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
diff = new(InstanceDiff)
|
diff = new(InstanceDiff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preserve the DestroyTainted flag
|
||||||
|
if n.Diff != nil {
|
||||||
|
diff.DestroyTainted = (*n.Diff).DestroyTainted
|
||||||
|
}
|
||||||
|
|
||||||
// Require a destroy if there is an ID and it requires new.
|
// Require a destroy if there is an ID and it requires new.
|
||||||
if diff.RequiresNew() && state != nil && state.ID != "" {
|
if diff.RequiresNew() && state != nil && state.ID != "" {
|
||||||
diff.Destroy = true
|
diff.Destroy = true
|
||||||
|
@ -135,7 +141,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our output
|
// Update our output
|
||||||
*n.Output = diff
|
*n.OutputDiff = diff
|
||||||
|
|
||||||
// Update the state if we care
|
// Update the state if we care
|
||||||
if n.OutputState != nil {
|
if n.OutputState != nil {
|
||||||
|
|
|
@ -285,7 +285,8 @@ func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
// EvalUndeposeState is an EvalNode implementation that reads the
|
// EvalUndeposeState is an EvalNode implementation that reads the
|
||||||
// InstanceState for a specific resource out of the state.
|
// InstanceState for a specific resource out of the state.
|
||||||
type EvalUndeposeState struct {
|
type EvalUndeposeState struct {
|
||||||
Name string
|
Name string
|
||||||
|
State **InstanceState
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
|
@ -316,7 +317,7 @@ func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
// Undepose
|
// Undepose
|
||||||
idx := len(rs.Deposed) - 1
|
idx := len(rs.Deposed) - 1
|
||||||
rs.Primary = rs.Deposed[idx]
|
rs.Primary = rs.Deposed[idx]
|
||||||
rs.Deposed[idx] = nil
|
rs.Deposed[idx] = *n.State
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,21 +88,6 @@ func TestEvalReadState(t *testing.T) {
|
||||||
},
|
},
|
||||||
ExpectedInstanceId: "i-abc123",
|
ExpectedInstanceId: "i-abc123",
|
||||||
},
|
},
|
||||||
"ReadStateTainted gets tainted instance": {
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.bar": &ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "i-abc123"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Node: &EvalReadStateTainted{
|
|
||||||
Name: "aws_instance.bar",
|
|
||||||
Output: &output,
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
ExpectedInstanceId: "i-abc123",
|
|
||||||
},
|
|
||||||
"ReadStateDeposed gets deposed instance": {
|
"ReadStateDeposed gets deposed instance": {
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
|
@ -175,32 +160,6 @@ restype.resname:
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvalWriteStateTainted(t *testing.T) {
|
|
||||||
state := &State{}
|
|
||||||
ctx := new(MockEvalContext)
|
|
||||||
ctx.StateState = state
|
|
||||||
ctx.StateLock = new(sync.RWMutex)
|
|
||||||
ctx.PathPath = rootModulePath
|
|
||||||
|
|
||||||
is := &InstanceState{ID: "i-abc123"}
|
|
||||||
node := &EvalWriteStateTainted{
|
|
||||||
Name: "restype.resname",
|
|
||||||
ResourceType: "restype",
|
|
||||||
State: &is,
|
|
||||||
Index: -1,
|
|
||||||
}
|
|
||||||
_, err := node.Eval(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Got err: %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkStateString(t, state, `
|
|
||||||
restype.resname: (1 tainted)
|
|
||||||
ID = <not created>
|
|
||||||
Tainted ID 1 = i-abc123
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalWriteStateDeposed(t *testing.T) {
|
func TestEvalWriteStateDeposed(t *testing.T) {
|
||||||
state := &State{}
|
state := &State{}
|
||||||
ctx := new(MockEvalContext)
|
ctx := new(MockEvalContext)
|
||||||
|
|
|
@ -295,16 +295,11 @@ provider.aws (close)
|
||||||
|
|
||||||
const testBuiltinGraphBuilderVerboseStr = `
|
const testBuiltinGraphBuilderVerboseStr = `
|
||||||
aws_instance.db
|
aws_instance.db
|
||||||
aws_instance.db (destroy tainted)
|
|
||||||
aws_instance.db (destroy)
|
aws_instance.db (destroy)
|
||||||
aws_instance.db (destroy tainted)
|
|
||||||
aws_instance.web (destroy tainted)
|
|
||||||
aws_instance.db (destroy)
|
aws_instance.db (destroy)
|
||||||
aws_instance.web (destroy)
|
aws_instance.web (destroy)
|
||||||
aws_instance.web
|
aws_instance.web
|
||||||
aws_instance.db
|
aws_instance.db
|
||||||
aws_instance.web (destroy tainted)
|
|
||||||
provider.aws
|
|
||||||
aws_instance.web (destroy)
|
aws_instance.web (destroy)
|
||||||
provider.aws
|
provider.aws
|
||||||
provider.aws
|
provider.aws
|
||||||
|
|
|
@ -120,7 +120,11 @@ func (n *GraphNodeConfigResource) VarWalk(fn func(config.InterpolatedVariable))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *GraphNodeConfigResource) Name() string {
|
func (n *GraphNodeConfigResource) Name() string {
|
||||||
return n.Resource.Id() + " (destroy)"
|
result := n.Resource.Id()
|
||||||
|
if n.Destroy {
|
||||||
|
result += " (destroy)"
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeDotter impl.
|
// GraphNodeDotter impl.
|
||||||
|
|
|
@ -42,7 +42,6 @@ type Resource struct {
|
||||||
State *InstanceState
|
State *InstanceState
|
||||||
Provisioners []*ResourceProvisionerConfig
|
Provisioners []*ResourceProvisionerConfig
|
||||||
Flags ResourceFlag
|
Flags ResourceFlag
|
||||||
TaintedIndex int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceKind specifies what kind of instance we're working with, whether
|
// ResourceKind specifies what kind of instance we're working with, whether
|
||||||
|
@ -53,7 +52,6 @@ const (
|
||||||
FlagPrimary ResourceFlag = 1 << iota
|
FlagPrimary ResourceFlag = 1 << iota
|
||||||
FlagTainted
|
FlagTainted
|
||||||
FlagOrphan
|
FlagOrphan
|
||||||
FlagHasTainted
|
|
||||||
FlagReplacePrimary
|
FlagReplacePrimary
|
||||||
FlagDeposed
|
FlagDeposed
|
||||||
)
|
)
|
||||||
|
|
|
@ -327,7 +327,7 @@ func (s *State) removeInstance(path []string, r *ResourceState, v *InstanceState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check lists
|
// Check lists
|
||||||
lists := [][]*InstanceState{r.Tainted, r.Deposed}
|
lists := [][]*InstanceState{r.Deposed}
|
||||||
for _, is := range lists {
|
for _, is := range lists {
|
||||||
for i, instance := range is {
|
for i, instance := range is {
|
||||||
if instance == v {
|
if instance == v {
|
||||||
|
@ -861,7 +861,11 @@ func (m *ModuleState) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, t := range rs.Deposed {
|
for idx, t := range rs.Deposed {
|
||||||
buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s\n", idx+1, t.ID))
|
taintStr := ""
|
||||||
|
if t.Tainted {
|
||||||
|
taintStr = " (tainted)"
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", idx+1, t.ID, taintStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rs.Dependencies) > 0 {
|
if len(rs.Dependencies) > 0 {
|
||||||
|
@ -1030,11 +1034,14 @@ type ResourceState struct {
|
||||||
// Deposed is used in the mechanics of CreateBeforeDestroy: the existing
|
// Deposed is used in the mechanics of CreateBeforeDestroy: the existing
|
||||||
// Primary is Deposed to get it out of the way for the replacement Primary to
|
// Primary is Deposed to get it out of the way for the replacement Primary to
|
||||||
// be created by Apply. If the replacement Primary creates successfully, the
|
// be created by Apply. If the replacement Primary creates successfully, the
|
||||||
// Deposed instance is cleaned up. If there were problems creating the
|
// Deposed instance is cleaned up.
|
||||||
// replacement, the instance remains in the Deposed list so it can be
|
//
|
||||||
// destroyed in a future run. Functionally, Deposed instances are very
|
// If there were problems creating the replacement Primary, the Deposed
|
||||||
// similar to Tainted instances in that Terraform is only tracking them in
|
// instance and the (now tainted) replacement Primary will be swapped so the
|
||||||
// order to remember to destroy them.
|
// tainted replacement will be cleaned up instead.
|
||||||
|
//
|
||||||
|
// An instance will remain in the Deposed list until it is successfully
|
||||||
|
// destroyed and purged.
|
||||||
Deposed []*InstanceState `json:"deposed,omitempty"`
|
Deposed []*InstanceState `json:"deposed,omitempty"`
|
||||||
|
|
||||||
// Provider is used when a resource is connected to a provider with an alias.
|
// Provider is used when a resource is connected to a provider with an alias.
|
||||||
|
@ -1071,22 +1078,21 @@ func (s *ResourceState) Equal(other *ResourceState) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tainted
|
|
||||||
if s.Primary.Tainted != other.Primary.Tainted {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taint marks a resource as tainted.
|
// Taint marks a resource as tainted.
|
||||||
func (r *ResourceState) Taint() {
|
func (r *ResourceState) Taint() {
|
||||||
r.Primary.Tainted = true
|
if r.Primary != nil {
|
||||||
|
r.Primary.Tainted = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untaint unmarks a resource as tainted.
|
// Untaint unmarks a resource as tainted.
|
||||||
func (r *ResourceState) Untaint() {
|
func (r *ResourceState) Untaint() {
|
||||||
r.Primary.Tainted = false
|
if r.Primary != nil {
|
||||||
|
r.Primary.Tainted = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceState) init() {
|
func (r *ResourceState) init() {
|
||||||
|
|
|
@ -133,19 +133,6 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, instance := range r.Tainted {
|
|
||||||
if f.relevant(a, instance) {
|
|
||||||
addr.InstanceType = TypeTainted
|
|
||||||
addr.InstanceTypeSet = true
|
|
||||||
results = append(results, &StateFilterResult{
|
|
||||||
Path: addr.Path,
|
|
||||||
Address: addr.String(),
|
|
||||||
Parent: resourceResult,
|
|
||||||
Value: instance,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, instance := range r.Deposed {
|
for _, instance := range r.Deposed {
|
||||||
if f.relevant(a, instance) {
|
if f.relevant(a, instance) {
|
||||||
addr.InstanceType = TypeDeposed
|
addr.InstanceType = TypeDeposed
|
||||||
|
|
|
@ -742,11 +742,14 @@ func TestResourceStateEqual(t *testing.T) {
|
||||||
{
|
{
|
||||||
false,
|
false,
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: nil,
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "foo"},
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -754,28 +757,15 @@ func TestResourceStateEqual(t *testing.T) {
|
||||||
{
|
{
|
||||||
true,
|
true,
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "foo"},
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "foo"},
|
ID: "foo",
|
||||||
},
|
Tainted: true,
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
true,
|
|
||||||
&ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -801,28 +791,29 @@ func TestResourceStateTaint(t *testing.T) {
|
||||||
&ResourceState{},
|
&ResourceState{},
|
||||||
},
|
},
|
||||||
|
|
||||||
"primary, no tainted": {
|
"primary, not tainted": {
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Primary: &InstanceState{ID: "foo"},
|
Primary: &InstanceState{ID: "foo"},
|
||||||
},
|
},
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "foo"},
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"primary, with tainted": {
|
"primary, tainted": {
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Primary: &InstanceState{ID: "foo"},
|
Primary: &InstanceState{
|
||||||
Tainted: []*InstanceState{
|
ID: "foo",
|
||||||
&InstanceState{ID: "bar"},
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&ResourceState{
|
&ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "bar"},
|
ID: "foo",
|
||||||
&InstanceState{ID: "foo"},
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -841,92 +832,36 @@ func TestResourceStateTaint(t *testing.T) {
|
||||||
func TestResourceStateUntaint(t *testing.T) {
|
func TestResourceStateUntaint(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
Input *ResourceState
|
Input *ResourceState
|
||||||
Index func() int
|
|
||||||
ExpectedOutput *ResourceState
|
ExpectedOutput *ResourceState
|
||||||
ExpectedErrMsg string
|
|
||||||
}{
|
}{
|
||||||
"no primary, no tainted, err": {
|
"no primary, err": {
|
||||||
Input: &ResourceState{},
|
Input: &ResourceState{},
|
||||||
ExpectedOutput: &ResourceState{},
|
ExpectedOutput: &ResourceState{},
|
||||||
ExpectedErrMsg: "Nothing to untaint",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"one tainted, no primary": {
|
"primary, not tainted": {
|
||||||
Input: &ResourceState{
|
Input: &ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{ID: "foo"},
|
||||||
&InstanceState{ID: "foo"},
|
},
|
||||||
|
ExpectedOutput: &ResourceState{
|
||||||
|
Primary: &InstanceState{ID: "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"primary, tainted": {
|
||||||
|
Input: &ResourceState{
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ExpectedOutput: &ResourceState{
|
ExpectedOutput: &ResourceState{
|
||||||
Primary: &InstanceState{ID: "foo"},
|
Primary: &InstanceState{ID: "foo"},
|
||||||
Tainted: []*InstanceState{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"one tainted, existing primary error": {
|
|
||||||
Input: &ResourceState{
|
|
||||||
Primary: &InstanceState{ID: "foo"},
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectedErrMsg: "Resource has a primary",
|
|
||||||
},
|
|
||||||
|
|
||||||
"multiple tainted, no index": {
|
|
||||||
Input: &ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "bar"},
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExpectedErrMsg: "please specify an index",
|
|
||||||
},
|
|
||||||
|
|
||||||
"multiple tainted, with index": {
|
|
||||||
Input: &ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "bar"},
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Index: func() int { return 1 },
|
|
||||||
ExpectedOutput: &ResourceState{
|
|
||||||
Primary: &InstanceState{ID: "foo"},
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "bar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"index out of bounds error": {
|
|
||||||
Input: &ResourceState{
|
|
||||||
Tainted: []*InstanceState{
|
|
||||||
&InstanceState{ID: "bar"},
|
|
||||||
&InstanceState{ID: "foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Index: func() int { return 2 },
|
|
||||||
ExpectedErrMsg: "out of range",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
index := -1
|
tc.Input.Untaint()
|
||||||
if tc.Index != nil {
|
|
||||||
index = tc.Index()
|
|
||||||
}
|
|
||||||
err := tc.Input.Untaint(index)
|
|
||||||
if tc.ExpectedErrMsg == "" && err != nil {
|
|
||||||
t.Fatalf("[%s] unexpected err: %s", k, err)
|
|
||||||
}
|
|
||||||
if tc.ExpectedErrMsg != "" {
|
|
||||||
if strings.Contains(err.Error(), tc.ExpectedErrMsg) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Fatalf("[%s] expected err: %s to contain: %s",
|
|
||||||
k, err, tc.ExpectedErrMsg)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
|
if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
|
||||||
t.Fatalf(
|
t.Fatalf(
|
||||||
"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
|
"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
|
||||||
|
@ -1152,7 +1087,7 @@ func TestInstanceState_MergeDiff(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInstanceState_MergeDiff_nil(t *testing.T) {
|
func TestInstanceState_MergeDiff_nil(t *testing.T) {
|
||||||
var is *InstanceState = nil
|
var is *InstanceState
|
||||||
|
|
||||||
diff := &InstanceDiff{
|
diff := &InstanceDiff{
|
||||||
Attributes: map[string]*ResourceAttrDiff{
|
Attributes: map[string]*ResourceAttrDiff{
|
||||||
|
@ -1504,7 +1439,7 @@ func TestUpgradeV0State(t *testing.T) {
|
||||||
foo.Primary.Attributes["key"] != "val" {
|
foo.Primary.Attributes["key"] != "val" {
|
||||||
t.Fatalf("bad: %#v", foo)
|
t.Fatalf("bad: %#v", foo)
|
||||||
}
|
}
|
||||||
if len(foo.Tainted) > 0 {
|
if foo.Primary.Tainted {
|
||||||
t.Fatalf("bad: %#v", foo)
|
t.Fatalf("bad: %#v", foo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1512,16 +1447,9 @@ func TestUpgradeV0State(t *testing.T) {
|
||||||
if bar.Type != "test_resource" {
|
if bar.Type != "test_resource" {
|
||||||
t.Fatalf("bad: %#v", bar)
|
t.Fatalf("bad: %#v", bar)
|
||||||
}
|
}
|
||||||
if bar.Primary != nil {
|
if !bar.Primary.Tainted {
|
||||||
t.Fatalf("bad: %#v", bar)
|
t.Fatalf("bad: %#v", bar)
|
||||||
}
|
}
|
||||||
if len(bar.Tainted) != 1 {
|
|
||||||
t.Fatalf("bad: %#v", bar)
|
|
||||||
}
|
|
||||||
bt := bar.Tainted[0]
|
|
||||||
if bt.ID != "1234" || bt.Attributes["a"] != "b" {
|
|
||||||
t.Fatalf("bad: %#v", bt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseResourceStateKey(t *testing.T) {
|
func TestParseResourceStateKey(t *testing.T) {
|
||||||
|
|
|
@ -399,9 +399,8 @@ aws_instance.foo:
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTerraformApplyProvisionerFailStr = `
|
const testTerraformApplyProvisionerFailStr = `
|
||||||
aws_instance.bar: (1 tainted)
|
aws_instance.bar: (tainted)
|
||||||
ID = <not created>
|
ID = foo
|
||||||
Tainted ID 1 = foo
|
|
||||||
aws_instance.foo:
|
aws_instance.foo:
|
||||||
ID = foo
|
ID = foo
|
||||||
num = 2
|
num = 2
|
||||||
|
@ -409,9 +408,8 @@ aws_instance.foo:
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTerraformApplyProvisionerFailCreateStr = `
|
const testTerraformApplyProvisionerFailCreateStr = `
|
||||||
aws_instance.bar: (1 tainted)
|
aws_instance.bar: (tainted)
|
||||||
ID = <not created>
|
ID = foo
|
||||||
Tainted ID 1 = foo
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTerraformApplyProvisionerFailCreateNoIdStr = `
|
const testTerraformApplyProvisionerFailCreateNoIdStr = `
|
||||||
|
@ -419,10 +417,10 @@ const testTerraformApplyProvisionerFailCreateNoIdStr = `
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTerraformApplyProvisionerFailCreateBeforeDestroyStr = `
|
const testTerraformApplyProvisionerFailCreateBeforeDestroyStr = `
|
||||||
aws_instance.bar: (1 tainted)
|
aws_instance.bar: (1 deposed)
|
||||||
ID = bar
|
ID = bar
|
||||||
require_new = abc
|
require_new = abc
|
||||||
Tainted ID 1 = foo
|
Deposed ID 1 = foo (tainted)
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTerraformApplyProvisionerResourceRefStr = `
|
const testTerraformApplyProvisionerResourceRefStr = `
|
||||||
|
@ -1242,9 +1240,8 @@ DESTROY/CREATE: aws_instance.bar
|
||||||
|
|
||||||
STATE:
|
STATE:
|
||||||
|
|
||||||
aws_instance.bar: (1 tainted)
|
aws_instance.bar: (tainted)
|
||||||
ID = <not created>
|
ID = baz
|
||||||
Tainted ID 1 = baz
|
|
||||||
aws_instance.foo:
|
aws_instance.foo:
|
||||||
ID = bar
|
ID = bar
|
||||||
num = 2
|
num = 2
|
||||||
|
|
|
@ -359,8 +359,9 @@ func TestPruneDestroyTransformer_tainted(t *testing.T) {
|
||||||
Path: RootModulePath,
|
Path: RootModulePath,
|
||||||
Resources: map[string]*ResourceState{
|
Resources: map[string]*ResourceState{
|
||||||
"aws_instance.bar": &ResourceState{
|
"aws_instance.bar": &ResourceState{
|
||||||
Tainted: []*InstanceState{
|
Primary: &InstanceState{
|
||||||
&InstanceState{ID: "foo"},
|
ID: "foo",
|
||||||
|
Tainted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -399,16 +400,11 @@ func TestPruneDestroyTransformer_tainted(t *testing.T) {
|
||||||
|
|
||||||
const testTransformDestroyBasicStr = `
|
const testTransformDestroyBasicStr = `
|
||||||
aws_instance.bar
|
aws_instance.bar
|
||||||
aws_instance.bar (destroy tainted)
|
|
||||||
aws_instance.bar (destroy)
|
aws_instance.bar (destroy)
|
||||||
aws_instance.foo
|
aws_instance.foo
|
||||||
aws_instance.bar (destroy tainted)
|
|
||||||
aws_instance.bar (destroy)
|
aws_instance.bar (destroy)
|
||||||
aws_instance.foo
|
aws_instance.foo
|
||||||
aws_instance.foo (destroy tainted)
|
|
||||||
aws_instance.foo (destroy)
|
aws_instance.foo (destroy)
|
||||||
aws_instance.foo (destroy tainted)
|
|
||||||
aws_instance.bar (destroy tainted)
|
|
||||||
aws_instance.foo (destroy)
|
aws_instance.foo (destroy)
|
||||||
aws_instance.bar (destroy)
|
aws_instance.bar (destroy)
|
||||||
`
|
`
|
||||||
|
@ -456,40 +452,28 @@ aws_instance.foo-bar (destroy)
|
||||||
|
|
||||||
const testTransformPruneDestroyTaintedStr = `
|
const testTransformPruneDestroyTaintedStr = `
|
||||||
aws_instance.bar
|
aws_instance.bar
|
||||||
aws_instance.bar (destroy tainted)
|
|
||||||
aws_instance.foo
|
aws_instance.foo
|
||||||
aws_instance.bar (destroy tainted)
|
|
||||||
aws_instance.foo
|
aws_instance.foo
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTransformCreateBeforeDestroyBasicStr = `
|
const testTransformCreateBeforeDestroyBasicStr = `
|
||||||
aws_instance.web
|
aws_instance.web
|
||||||
aws_instance.web (destroy tainted)
|
|
||||||
aws_instance.web (destroy tainted)
|
|
||||||
aws_load_balancer.lb (destroy tainted)
|
|
||||||
aws_instance.web (destroy)
|
aws_instance.web (destroy)
|
||||||
aws_instance.web
|
aws_instance.web
|
||||||
aws_load_balancer.lb
|
aws_load_balancer.lb
|
||||||
aws_load_balancer.lb (destroy)
|
aws_load_balancer.lb (destroy)
|
||||||
aws_load_balancer.lb
|
aws_load_balancer.lb
|
||||||
aws_instance.web
|
aws_instance.web
|
||||||
aws_load_balancer.lb (destroy tainted)
|
|
||||||
aws_load_balancer.lb (destroy)
|
aws_load_balancer.lb (destroy)
|
||||||
aws_load_balancer.lb (destroy tainted)
|
|
||||||
aws_load_balancer.lb (destroy)
|
aws_load_balancer.lb (destroy)
|
||||||
`
|
`
|
||||||
|
|
||||||
const testTransformCreateBeforeDestroyTwiceStr = `
|
const testTransformCreateBeforeDestroyTwiceStr = `
|
||||||
aws_autoscale.bar
|
aws_autoscale.bar
|
||||||
aws_autoscale.bar (destroy tainted)
|
|
||||||
aws_lc.foo
|
aws_lc.foo
|
||||||
aws_autoscale.bar (destroy tainted)
|
|
||||||
aws_autoscale.bar (destroy)
|
aws_autoscale.bar (destroy)
|
||||||
aws_autoscale.bar
|
aws_autoscale.bar
|
||||||
aws_lc.foo
|
aws_lc.foo
|
||||||
aws_lc.foo (destroy tainted)
|
|
||||||
aws_lc.foo (destroy tainted)
|
|
||||||
aws_autoscale.bar (destroy tainted)
|
|
||||||
aws_lc.foo (destroy)
|
aws_lc.foo (destroy)
|
||||||
aws_autoscale.bar
|
aws_autoscale.bar
|
||||||
aws_autoscale.bar (destroy)
|
aws_autoscale.bar (destroy)
|
||||||
|
|
|
@ -337,7 +337,7 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,
|
||||||
Config: &resourceConfig,
|
Config: &resourceConfig,
|
||||||
Provider: &provider,
|
Provider: &provider,
|
||||||
State: &state,
|
State: &state,
|
||||||
Output: &diff,
|
OutputDiff: &diff,
|
||||||
OutputState: &state,
|
OutputState: &state,
|
||||||
},
|
},
|
||||||
&EvalCheckPreventDestroy{
|
&EvalCheckPreventDestroy{
|
||||||
|
@ -392,7 +392,7 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,
|
||||||
// Apply
|
// Apply
|
||||||
var diffApply *InstanceDiff
|
var diffApply *InstanceDiff
|
||||||
var err error
|
var err error
|
||||||
var createNew, tainted bool
|
var createNew bool
|
||||||
var createBeforeDestroyEnabled bool
|
var createBeforeDestroyEnabled bool
|
||||||
var wasChangeType DiffChangeType
|
var wasChangeType DiffChangeType
|
||||||
nodes = append(nodes, &EvalOpFilter{
|
nodes = append(nodes, &EvalOpFilter{
|
||||||
|
@ -456,11 +456,12 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,
|
||||||
},
|
},
|
||||||
|
|
||||||
&EvalDiff{
|
&EvalDiff{
|
||||||
Info: info,
|
Info: info,
|
||||||
Config: &resourceConfig,
|
Config: &resourceConfig,
|
||||||
Provider: &provider,
|
Provider: &provider,
|
||||||
State: &state,
|
Diff: &diffApply,
|
||||||
Output: &diffApply,
|
State: &state,
|
||||||
|
OutputDiff: &diffApply,
|
||||||
},
|
},
|
||||||
&EvalIgnoreChanges{
|
&EvalIgnoreChanges{
|
||||||
Resource: n.Resource,
|
Resource: n.Resource,
|
||||||
|
@ -511,20 +512,22 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,
|
||||||
Resource: n.Resource,
|
Resource: n.Resource,
|
||||||
InterpResource: resource,
|
InterpResource: resource,
|
||||||
CreateNew: &createNew,
|
CreateNew: &createNew,
|
||||||
Tainted: &tainted,
|
|
||||||
Error: &err,
|
Error: &err,
|
||||||
},
|
},
|
||||||
&EvalIf{
|
&EvalIf{
|
||||||
If: func(ctx EvalContext) (bool, error) {
|
If: func(ctx EvalContext) (bool, error) {
|
||||||
if createBeforeDestroyEnabled {
|
return createBeforeDestroyEnabled && err != nil, nil
|
||||||
tainted = err != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
failure := tainted || err != nil
|
|
||||||
return createBeforeDestroyEnabled && failure, nil
|
|
||||||
},
|
},
|
||||||
Then: &EvalUndeposeState{
|
Then: &EvalUndeposeState{
|
||||||
Name: n.stateId(),
|
Name: n.stateId(),
|
||||||
|
State: &state,
|
||||||
|
},
|
||||||
|
Else: &EvalWriteState{
|
||||||
|
Name: n.stateId(),
|
||||||
|
ResourceType: n.Resource.Type,
|
||||||
|
Provider: n.Resource.Provider,
|
||||||
|
Dependencies: n.StateDependencies(),
|
||||||
|
State: &state,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -536,13 +539,6 @@ func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource,
|
||||||
Diff: nil,
|
Diff: nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
&EvalWriteState{
|
|
||||||
Name: n.stateId(),
|
|
||||||
ResourceType: n.Resource.Type,
|
|
||||||
Provider: n.Resource.Provider,
|
|
||||||
Dependencies: n.StateDependencies(),
|
|
||||||
State: &state,
|
|
||||||
},
|
|
||||||
&EvalApplyPost{
|
&EvalApplyPost{
|
||||||
Info: info,
|
Info: info,
|
||||||
State: &state,
|
State: &state,
|
||||||
|
|
Loading…
Reference in New Issue