Skip to content

Commit

Permalink
Modifying underlying logic behind TestStep.Taint to call the terrafor…
Browse files Browse the repository at this point in the history
…m taint command
  • Loading branch information
bendbennett committed Aug 12, 2022
1 parent 7f4847b commit 8205fef
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 147 deletions.
17 changes: 5 additions & 12 deletions helper/resource/testing_config.go
Expand Up @@ -2,31 +2,24 @@ package resource

import (
"context"
"errors"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest"
)

func testStepTaint(ctx context.Context, state *terraform.State, step TestStep) error {
func testStepTaint(ctx context.Context, step TestStep, wd *plugintest.WorkingDir) error {
if len(step.Taint) == 0 {
return nil
}

logging.HelperResourceTrace(ctx, fmt.Sprintf("Using TestStep Taint: %v", step.Taint))

for _, p := range step.Taint {
m := state.RootModule()
if m == nil {
return errors.New("no state")
err := wd.Taint(ctx, p)
if err != nil {
return fmt.Errorf("error tainting resource: %s", err)
}
rs, ok := m.Resources[p]
if !ok {
return fmt.Errorf("resource %q not found in state", p)
}
logging.HelperResourceWarn(ctx, fmt.Sprintf("Explicitly tainting resource %q", p))
rs.Taint()
}
return nil
}
26 changes: 2 additions & 24 deletions helper/resource/testing_new.go
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/davecgh/go-spew/spew"
tfjson "github.com/hashicorp/terraform-json"
testing "github.com/mitchellh/go-testing-interface"
"github.com/mitchellh/go-testing-interface"

"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest"
Expand Down Expand Up @@ -152,29 +152,7 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
}

if step.Config != "" && !step.Destroy && len(step.Taint) > 0 {
var state *terraform.State

err := runProviderCommand(ctx, t, func() error {
var err error

state, err = getState(ctx, t, wd)

if err != nil {
return err
}

return nil
}, wd, providers)

if err != nil {
logging.HelperResourceError(ctx,
"TestStep error reading prior state before tainting resources",
map[string]interface{}{logging.KeyError: err},
)
t.Fatalf("TestStep %d/%d error reading prior state before tainting resources: %s", stepNumber, len(c.Steps), err)
}

err = testStepTaint(ctx, state, step)
err := testStepTaint(ctx, step, wd)

if err != nil {
logging.HelperResourceError(ctx,
Expand Down
62 changes: 62 additions & 0 deletions helper/resource/teststep_providers_test.go
Expand Up @@ -10,8 +10,10 @@ import (
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestStepProviderConfig(t *testing.T) {
Expand Down Expand Up @@ -342,6 +344,66 @@ func TestTest_TestStep_ExternalProviders_To_ProviderFactories_StateUpgraders(t *
})
}

func TestTest_TestStep_Taint(t *testing.T) {
t.Parallel()

var idOne, idTwo string

Test(t, TestCase{
ExternalProviders: map[string]ExternalProvider{
"random": {
Source: "registry.terraform.io/hashicorp/random",
VersionConstraint: "3.3.2",
},
},
Steps: []TestStep{
{
Config: `resource "random_string" "test" {
length = 10
}`,
Check: ComposeAggregateTestCheckFunc(
TestCheckResourceAttr("random_string.test", "length", "10"),
ExtractResourceAttr("random_string.test", "id", &idOne),
),
},
{
Taint: []string{"random_string.test"},
Config: `resource "random_string" "test" {
length = 10
}`,
Check: ComposeAggregateTestCheckFunc(
TestCheckResourceAttr("random_string.test", "length", "10"),
ExtractResourceAttr("random_string.test", "id", &idTwo),
),
},
},
})

if idOne == idTwo {
t.Errorf("taint is not causing destroy-create cycle, idOne == idTwo: %s == %s", idOne, idTwo)
}
}

func ExtractResourceAttr(resourceName string, attributeName string, attributeValue *string) TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]

if !ok {
return fmt.Errorf("resource name %s not found in state", resourceName)
}

attrValue, ok := rs.Primary.Attributes[attributeName]

if !ok {
return fmt.Errorf("attribute %s not found in resource %s state", attributeName, resourceName)
}

*attributeValue = attrValue

return nil
}
}

func TestTest_TestStep_ProtoV5ProviderFactories(t *testing.T) {
t.Parallel()

Expand Down
12 changes: 12 additions & 0 deletions internal/plugintest/working_dir.go
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/hashicorp/terraform-exec/tfexec"
tfjson "github.com/hashicorp/terraform-json"

"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
)

Expand Down Expand Up @@ -303,6 +304,17 @@ func (wd *WorkingDir) Import(ctx context.Context, resource, id string) error {
return err
}

// Taint runs terraform taint
func (wd *WorkingDir) Taint(ctx context.Context, address string) error {
logging.HelperResourceTrace(ctx, "Calling Terraform CLI taint command")

err := wd.tf.Taint(context.Background(), address)

logging.HelperResourceTrace(ctx, "Called Terraform CLI import command")

return err
}

// Refresh runs terraform refresh
func (wd *WorkingDir) Refresh(ctx context.Context) error {
logging.HelperResourceTrace(ctx, "Calling Terraform CLI refresh command")
Expand Down
24 changes: 2 additions & 22 deletions terraform/state.go
Expand Up @@ -14,8 +14,8 @@ import (
"sync"

"github.com/hashicorp/go-cty/cty"
multierror "github.com/hashicorp/go-multierror"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-uuid"
"github.com/mitchellh/copystructure"

"github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs"
Expand Down Expand Up @@ -1225,26 +1225,6 @@ func (s *ResourceState) Equal(other *ResourceState) bool {
return s.Primary.Equal(other.Primary)
}

// Taint marks a resource as tainted.
func (s *ResourceState) Taint() {
s.Lock()
defer s.Unlock()

if s.Primary != nil {
s.Primary.Tainted = true
}
}

// Untaint unmarks a resource as tainted.
func (s *ResourceState) Untaint() {
s.Lock()
defer s.Unlock()

if s.Primary != nil {
s.Primary.Tainted = false
}
}

func (s *ResourceState) init() {
s.Lock()
defer s.Unlock()
Expand Down
89 changes: 0 additions & 89 deletions terraform/state_test.go
Expand Up @@ -907,95 +907,6 @@ func TestResourceStateEqual(t *testing.T) {
}
}

func TestResourceStateTaint(t *testing.T) {
cases := map[string]struct {
Input *ResourceState
Output *ResourceState
}{
"no primary": {
&ResourceState{},
&ResourceState{},
},

"primary, not tainted": {
&ResourceState{
Primary: &InstanceState{ID: "foo"},
},
&ResourceState{
Primary: &InstanceState{
ID: "foo",
Tainted: true,
},
},
},

"primary, tainted": {
&ResourceState{
Primary: &InstanceState{
ID: "foo",
Tainted: true,
},
},
&ResourceState{
Primary: &InstanceState{
ID: "foo",
Tainted: true,
},
},
},
}

for k, tc := range cases {
tc.Input.Taint()
if !reflect.DeepEqual(tc.Input, tc.Output) {
t.Fatalf(
"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
k, tc.Output, tc.Input)
}
}
}

func TestResourceStateUntaint(t *testing.T) {
cases := map[string]struct {
Input *ResourceState
ExpectedOutput *ResourceState
}{
"no primary, err": {
Input: &ResourceState{},
ExpectedOutput: &ResourceState{},
},

"primary, not tainted": {
Input: &ResourceState{
Primary: &InstanceState{ID: "foo"},
},
ExpectedOutput: &ResourceState{
Primary: &InstanceState{ID: "foo"},
},
},
"primary, tainted": {
Input: &ResourceState{
Primary: &InstanceState{
ID: "foo",
Tainted: true,
},
},
ExpectedOutput: &ResourceState{
Primary: &InstanceState{ID: "foo"},
},
},
}

for k, tc := range cases {
tc.Input.Untaint()
if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
t.Fatalf(
"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
k, tc.ExpectedOutput, tc.Input)
}
}
}

func TestInstanceStateEmpty(t *testing.T) {
cases := map[string]struct {
In *InstanceState
Expand Down

0 comments on commit 8205fef

Please sign in to comment.