Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfsdk: Check ProposedNewState for State differences before marking unknowns for Computed-only attributes in plans #184

Merged
merged 2 commits into from
Sep 29, 2021

Conversation

bflad
Copy link
Contributor

@bflad bflad commented Sep 28, 2021

Closes #181
Reference: https://github.com/hashicorp/terraform/blob/main/docs/resource-instance-change-lifecycle.md

ProposedNewState is the merged PriorState and Configuration. This allows us to verify if any configuration changes have occurred.

Previously, any Computed attributes would always trigger plan differences due to the unknown marking.

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # hashicups_order.edu will be updated in-place
  ~ resource "hashicups_order" "edu" {
      ~ id           = "1" -> (known after apply)
      ~ items        = [
          ~ {
            ~ coffee   = {
              + description = (known after apply)
                id          = 3
              ~ image       = "/nomad.png" -> (known after apply)
              ~ name        = "Nomadicano" -> (known after apply)
              ~ price       = 150 -> (known after apply)
              ~ teaser      = "Drink one today and you will want to schedule another" -> (known after apply)
            }
              # (1 unchanged attribute hidden)

            ~ coffee   = {
              + description = (known after apply)
                id          = 1
              ~ image       = "/packer.png" -> (known after apply)
              ~ name        = "Packer Spiced Latte" -> (known after apply)
              ~ price       = 350 -> (known after apply)
              ~ teaser      = "Packed with goodness to spice up your images" -> (known after apply)
            }
              # (2 unchanged attributes hidden)
          },
        ]
      ~ last_updated = "Tuesday, 28-Sep-21 11:53:27 EDT" -> (known after apply)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

This change limits the unknown marking to only be performed when there is actually a change between the ProposedNewState and State.

Without a configuration change:

No changes. Your infrastructure matches the configuration.

With a configuration change:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # hashicups_order.edu will be updated in-place
  ~ resource "hashicups_order" "edu" {
      ~ id           = "1" -> (known after apply)
      ~ items        = [
          ~ {
            ~ coffee   = {
              + description = (known after apply)
                id          = 3
              ~ image       = "/nomad.png" -> (known after apply)
              ~ name        = "Nomadicano" -> (known after apply)
              ~ price       = 150 -> (known after apply)
              ~ teaser      = "Drink one today and you will want to schedule another" -> (known after apply)
            }
              # (1 unchanged attribute hidden)

            ~ coffee   = {
              + description = (known after apply)
              ~ id          = 1 -> 2
              ~ image       = "/packer.png" -> (known after apply)
              ~ name        = "Packer Spiced Latte" -> (known after apply)
              ~ price       = 350 -> (known after apply)
              ~ teaser      = "Packed with goodness to spice up your images" -> (known after apply)
            }
              # (2 unchanged attributes hidden)
          },
        ]
      ~ last_updated = "Tuesday, 28-Sep-21 11:53:27 EDT" -> (known after apply)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

As a followup, it may be necessary to invoke two passes of attribute and resource plan modifiers to ensure any changes captured by those modifiers triggers the unknown marking. As it stands now, a plan with no configuration changes, but changes due to plan modifiers will not correctly trigger the unknown marking. See also: #183

…knowns for Computed-only attributes in plans

Reference: #181

Previously, any `Computed` attributes would always trigger plan differences due to the unknown marking.

```
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # hashicups_order.edu will be updated in-place
  ~ resource "hashicups_order" "edu" {
      ~ id           = "1" -> (known after apply)
      ~ items        = [
          ~ {
            ~ coffee   = {
              + description = (known after apply)
                id          = 3
              ~ image       = "/nomad.png" -> (known after apply)
              ~ name        = "Nomadicano" -> (known after apply)
              ~ price       = 150 -> (known after apply)
              ~ teaser      = "Drink one today and you will want to schedule another" -> (known after apply)
            }
              # (1 unchanged attribute hidden)

            ~ coffee   = {
              + description = (known after apply)
                id          = 1
              ~ image       = "/packer.png" -> (known after apply)
              ~ name        = "Packer Spiced Latte" -> (known after apply)
              ~ price       = 350 -> (known after apply)
              ~ teaser      = "Packed with goodness to spice up your images" -> (known after apply)
            }
              # (2 unchanged attributes hidden)
          },
        ]
      ~ last_updated = "Tuesday, 28-Sep-21 11:53:27 EDT" -> (known after apply)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
```

This change limits the unknown marking to only be performed when there is actually a change between the `ProposedNewState` and `State`.

Without a configuration change:

```
No changes. Your infrastructure matches the configuration.
```

With a configuration change:

```
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # hashicups_order.edu will be updated in-place
  ~ resource "hashicups_order" "edu" {
      ~ id           = "1" -> (known after apply)
      ~ items        = [
          ~ {
            ~ coffee   = {
              + description = (known after apply)
                id          = 3
              ~ image       = "/nomad.png" -> (known after apply)
              ~ name        = "Nomadicano" -> (known after apply)
              ~ price       = 150 -> (known after apply)
              ~ teaser      = "Drink one today and you will want to schedule another" -> (known after apply)
            }
              # (1 unchanged attribute hidden)

            ~ coffee   = {
              + description = (known after apply)
              ~ id          = 1 -> 2
              ~ image       = "/packer.png" -> (known after apply)
              ~ name        = "Packer Spiced Latte" -> (known after apply)
              ~ price       = 350 -> (known after apply)
              ~ teaser      = "Packed with goodness to spice up your images" -> (known after apply)
            }
              # (2 unchanged attributes hidden)
          },
        ]
      ~ last_updated = "Tuesday, 28-Sep-21 11:53:27 EDT" -> (known after apply)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
```

As a followup, it may be necessary to invoke two passes of attribute and resource plan modifiers to ensure any changes captured by those modifiers triggers the unknown marking. As it stands now, a plan with no configuration changes, but changes due to plan modifiers will not correctly trigger the unknown marking. See also: #183
@bflad bflad added the bug Something isn't working label Sep 28, 2021
@bflad bflad added this to the v0.4.2 milestone Sep 28, 2021
@bflad bflad requested a review from a team September 28, 2021 19:38
Copy link
Contributor Author

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some additional breadcrumbs for review, since the real change was just adding && !plan.Equal(state)

@@ -1719,6 +1719,7 @@ func TestServerPlanResourceChange(t *testing.T) {
"favorite_colors": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "red"),
tftypes.NewValue(tftypes.String, "orange"),
tftypes.NewValue(tftypes.String, "yellow"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test fix since the configuration contained it, so the proposedNewState would also contain it.

@@ -1738,10 +1739,47 @@ func TestServerPlanResourceChange(t *testing.T) {
"favorite_colors": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "red"),
tftypes.NewValue(tftypes.String, "orange"),
tftypes.NewValue(tftypes.String, "yellow"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test fix since the proposed new state contained it, so the planned state would also contain it.

@@ -1783,7 +1821,199 @@ func TestServerPlanResourceChange(t *testing.T) {
resourceType: testServeResourceTypeTwoType,
expectedPlannedState: tftypes.NewValue(testServeResourceTypeTwoType, nil),
},
"three_nested_computed_unknown": {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: This was renamed to three_nested_computed_nested_configuration_change as seen below the next Git diff, just to capture the additional nuance between the two new test cases and the original.

@@ -92,6 +92,10 @@ func (rt testServeResourceTypeAttributePlanModifiers) GetSchema(_ context.Contex
Type: types.StringType,
PlanModifiers: []AttributePlanModifier{testAttrDefaultValueModifier{}},
},
"computed_string_no_modifiers": {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional attribute on this resource to allow us to further verify plan unknown marking, since this resource has attribute plan modifiers and allowed me to catch #183 for the future. It does not have to be part of this pull request though. 😄

@kmoe
Copy link
Member

kmoe commented Sep 29, 2021

Getting a slightly unexpected result with this.

Configuration:

provider "hashicups" {
  username = "education"
  password = "test123"
  host     = "http://localhost:19090"
}

resource "hashicups_order" "edu" {
  items = [{
    coffee = {
      id = 3
    }
    quantity = 2
    }, {
    coffee = {
      id = 2
    }
    quantity = 2
    }
  ]
}

Steps

  1. Run terraform apply and proceed with yes
  2. Change the second coffee id to 1
  3. Run terraform apply and proceed with yes

Result

The output from the second apply is as in the PR description, but when actually running the apply we get this:

  Enter a value: yes

hashicups_order.edu: Modifying... [id=1]
╷
│ Error: Provider produced inconsistent result after apply
│ 
│ When applying changes to hashicups_order.edu, provider
│ "provider[\"registry.terraform.io/hashicorp/hashicups\"]" produced an unexpected new value: .items: was
│ cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"coffee":cty.ObjectVal(map[string]cty.Value{"description":cty.UnknownVal(cty.String),
│ "id":cty.NumberIntVal(3), "image":cty.UnknownVal(cty.String), "name":cty.UnknownVal(cty.String),
│ "price":cty.UnknownVal(cty.Number), "teaser":cty.UnknownVal(cty.String)}), "quantity":cty.NumberIntVal(2)}),
│ cty.ObjectVal(map[string]cty.Value{"coffee":cty.ObjectVal(map[string]cty.Value{"description":cty.UnknownVal(cty.String),
│ "id":cty.NumberIntVal(2), "image":cty.UnknownVal(cty.String), "name":cty.UnknownVal(cty.String),
│ "price":cty.UnknownVal(cty.Number), "teaser":cty.UnknownVal(cty.String)}),
│ "quantity":cty.NumberIntVal(2)})}), but now null.
│ 
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.

Edit: This error on apply also happens with main and v0.4.1. I'm not sure why .items is being set to null.

Edit 2: This appears to be a bug in the hashicups provider or server.

@kmoe kmoe merged commit 7171bf2 into main Sep 29, 2021
@kmoe kmoe deleted the bflad-b-PlanResourceChange-Computed-permadiff branch September 29, 2021 13:41
@github-actions
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PlanResourceChange Always Showing Differences
2 participants