Merge pull request #18125 from hashicorp/f-testing-taint

helper/resource: Add ability to pre-taint resources
This commit is contained in:
Chris Marchesi 2018-08-01 09:31:07 -07:00 committed by GitHub
commit 67182d06d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 0 deletions

View File

@ -266,6 +266,15 @@ type TestStep struct {
// below.
PreConfig func()
// Taint is a list of resource addresses to taint prior to the execution of
// the step. Be sure to only include this at a step where the referenced
// address will be present in state, as it will fail the test if the resource
// is missing.
//
// This option is ignored on ImportState tests, and currently only works for
// resources in the root module path.
Taint []string
//---------------------------------------------------------------
// Test modes. One of the following groups of settings must be
// set to determine what the test step will do. Ideally we would've

View File

@ -1,6 +1,7 @@
package resource
import (
"errors"
"fmt"
"log"
"strings"
@ -21,6 +22,14 @@ func testStep(
opts terraform.ContextOpts,
state *terraform.State,
step TestStep) (*terraform.State, error) {
// Pre-taint any resources that have been defined in Taint, as long as this
// is not a destroy step.
if !step.Destroy {
if err := testStepTaint(state, step); err != nil {
return state, err
}
}
mod, err := testModule(opts, step)
if err != nil {
return state, err
@ -154,3 +163,19 @@ func testStep(
// Made it here? Good job test step!
return state, nil
}
func testStepTaint(state *terraform.State, step TestStep) error {
for _, p := range step.Taint {
m := state.RootModule()
if m == nil {
return errors.New("no state")
}
rs, ok := m.Resources[p]
if !ok {
return fmt.Errorf("resource %q not found in state", p)
}
log.Printf("[WARN] Test: Explicitly tainting resource %q", p)
rs.Taint()
}
return nil
}

View File

@ -911,6 +911,85 @@ func mockSweeperFunc(s string) error {
return nil
}
func TestTest_Taint(t *testing.T) {
mp := testProvider()
mp.DiffFn = func(
_ *terraform.InstanceInfo,
state *terraform.InstanceState,
_ *terraform.ResourceConfig,
) (*terraform.InstanceDiff, error) {
return &terraform.InstanceDiff{
DestroyTainted: state.Tainted,
}, nil
}
mp.ApplyFn = func(
info *terraform.InstanceInfo,
state *terraform.InstanceState,
diff *terraform.InstanceDiff,
) (*terraform.InstanceState, error) {
var id string
switch {
case diff.Destroy && !diff.DestroyTainted:
return nil, nil
case diff.DestroyTainted:
id = "tainted"
default:
id = "not_tainted"
}
return &terraform.InstanceState{
ID: id,
}, nil
}
mp.RefreshFn = func(
_ *terraform.InstanceInfo,
state *terraform.InstanceState,
) (*terraform.InstanceState, error) {
return state, nil
}
mt := new(mockT)
Test(mt, TestCase{
Providers: map[string]terraform.ResourceProvider{
"test": mp,
},
Steps: []TestStep{
TestStep{
Config: testConfigStr,
Check: func(s *terraform.State) error {
rs := s.RootModule().Resources["test_instance.foo"]
if rs.Primary.ID != "not_tainted" {
return fmt.Errorf("expected not_tainted, got %s", rs.Primary.ID)
}
return nil
},
},
TestStep{
Taint: []string{"test_instance.foo"},
Config: testConfigStr,
Check: func(s *terraform.State) error {
rs := s.RootModule().Resources["test_instance.foo"]
if rs.Primary.ID != "tainted" {
return fmt.Errorf("expected tainted, got %s", rs.Primary.ID)
}
return nil
},
},
TestStep{
Taint: []string{"test_instance.fooo"},
Config: testConfigStr,
ExpectError: regexp.MustCompile("resource \"test_instance.fooo\" not found in state"),
},
},
})
if mt.failed() {
t.Fatalf("test failure: %s", mt.failMessage())
}
}
const testConfigStr = `
resource "test_instance" "foo" {}
`