Skip to content

Commit

Permalink
Add register CustomizeDiff to propagate changed values
Browse files Browse the repository at this point in the history
* Ensure that (non-empty) changes to content mark `value` as
computed so that the new value is propagated to resources that
use the value attribute (locals, outputs, other resources, etc)
* Add a test case to enforce a Terraform output get updated
  • Loading branch information
dghubble committed Mar 30, 2022
1 parent da83c2d commit 9136f37
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
29 changes: 24 additions & 5 deletions util/resource_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

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

Expand All @@ -17,48 +18,66 @@ func resourceRegister() *schema.Resource {
DeleteContext: registerDelete,
Schema: map[string]*schema.Schema{
"set": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
Type: schema.TypeString,
// Allow content to be null
Optional: true,
// ForceNew would create a new resource on any change,
// but this resource should ignore empty string or null
// changes
ForceNew: false,
// Suppress plan diffs setting content to an empty string
// or null. Empty changes are ignored / NoOps
DiffSuppressOnRefresh: true,
DiffSuppressFunc: registerDiffSuppress,
},
"value": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "Last set register value",
Description: "Computed register value",
},
},
// Changes to set (that are non-empty) mark value as computed
CustomizeDiff: customdiff.ComputedIf("value", func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
return d.HasChange("set") && d.Get("set").(string) != ""
}),
}
}

// registerCreate stores content as the register value.
func resourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics

set := d.Get("set").(string)
d.Set("value", set)
d.SetId(strconv.Itoa(hashcode.String(set)))
return diags
}

// registerRead sets register attributes
func registerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
// register has no remote API equivalent to check or attributes to set
return nil
}

// registerUpdate applies non-empty content changes to the value attribute.
func registerUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics

set := d.Get("set").(string)
if set != "" {
d.Set("value", set)
}
return diags
}

// registerDelete removes the resource from state.
func registerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
d.SetId("")
return nil
}

// registerDiffSuppress supresses plan diffs setting content to an empty
// string or null (converts to empty string in ResourceData). Empty content
// does not alter the register value.
func registerDiffSuppress(k, oldV, newV string, d *schema.ResourceData) bool {
return newV == ""
}
34 changes: 28 additions & 6 deletions util/resource_register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,38 @@ const registerInitial = `
resource "util_register" "example" {
set = "a1b2c3"
}
output "out" {
value = util_register.example.value
}
`

const registerUnsetSHA = `
resource "util_register" "example" {
set = ""
}
output "out" {
value = util_register.example.value
}
`

const registerUpdateSHA = `
resource "util_register" "example" {
set = "b2c3d4"
}
output "out" {
value = util_register.example.value
}
`

// register expected values
const (
registerInitialExpected = "a1b2c3"
registerUpdateExpected = "b2c3d4"
)

func TestRegister(t *testing.T) {
r.UnitTest(t, r.TestCase{
Providers: testProviders,
Expand All @@ -32,29 +50,33 @@ func TestRegister(t *testing.T) {
r.TestStep{
Config: registerInitial,
Check: r.ComposeTestCheckFunc(
r.TestCheckResourceAttr("util_register.example", "value", "a1b2c3"),
r.TestCheckResourceAttr("util_register.example", "value", registerInitialExpected),
r.TestCheckOutput("out", registerInitialExpected),
),
},
// set with empty value doesn't change value
// Empty content does NOT change value
r.TestStep{
Config: registerUnsetSHA,
Check: r.ComposeTestCheckFunc(
r.TestCheckResourceAttr("util_register.example", "value", "a1b2c3"),
r.TestCheckResourceAttr("util_register.example", "value", registerInitialExpected),
r.TestCheckOutput("out", registerInitialExpected),
),
},
// set with content updates values
// Non-empty content DOES change value
r.TestStep{
Config: registerUpdateSHA,
Check: r.ComposeTestCheckFunc(
r.TestCheckResourceAttr("util_register.example", "value", "b2c3d4"),
r.TestCheckResourceAttr("util_register.example", "value", registerUpdateExpected),
r.TestCheckOutput("out", registerUpdateExpected),
),
},
// suppress noisy diffs that won't affect value
r.TestStep{
Config: registerUnsetSHA,
PlanOnly: true,
Check: r.ComposeTestCheckFunc(
r.TestCheckResourceAttr("util_register.example", "value", "b2c3d4"),
r.TestCheckResourceAttr("util_register.example", "value", registerUpdateExpected),
r.TestCheckOutput("out", registerUpdateExpected),
),
},
},
Expand Down

0 comments on commit 9136f37

Please sign in to comment.