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

core: Add GetOkExists schema function #15723

Merged
merged 4 commits into from
Aug 3, 2017
Merged

Conversation

grubernaut
Copy link
Contributor

Adds GetOkExists as a schema function. This should only be used to verify
boolean attributes are either set or not set, regardless of their zero
value for their type. There are a few small use cases outside of the boolean
type where this will be helpful as well.

Overall, this shouldn't detract from the zero-value checks that GetOK()
currently has, and should only be used when absolutely needed. However,
there are enough use-cases for this addition without checking for the
zero-value of the type, that this is needed.

Primary use case is for a boolean attribute that is Optional and Computed,
without a default value. There's currently no way to verify that the boolean
attribute was explicitly set to the zero-value literal with the current
GetOk() function. This new function allows for that check, keeping the
Computed check for the returned exists boolean.

$ make test TEST=./helper/schema TESTARGS="-run=TestResourceDataGetOkExists"
==> Checking that code complies with gofmt requirements...
go generate $(go list ./... | grep -v /terraform/vendor/)
2017/08/03 14:13:30 Generated command/internal_plugin_list.go
go test -i ./helper/schema || exit 1
echo ./helper/schema | \
        xargs -t -n4 go test -run=TestResourceDataGetOkExists -timeout=60s -parallel=4
go test -run=TestResourceDataGetOkExists -timeout=60s -parallel=4 ./helper/schema 
ok      github.com/hashicorp/terraform/helper/schema    0.007s

Adds `GetOkRaw` as a schema function. This should only be used to verify
boolean attributes are either set or not set, regardless of their zero
value for their type. There are a few small use cases outside of the boolean
type where this will be helpful as well.

Overall, this shouldn't detract from the zero-value checks that `GetOK()`
currently has, and should only be used when absolutely needed. However,
there are enough use-cases for this addition without checking for the
zero-value of the type, that this is needed.

Primary use case is for a boolean attribute that is `Optional` and `Computed`,
without a default value. There's currently no way to verify that the boolean
attribute was explicitly set to the zero-value literal with the current
`GetOk()` function. This new function allows for that check, keeping the
`Computed` check for the returned `exists` boolean.

```
$ make test TEST=./helper/schema TESTARGS="-run=TestResourceDataGetOkRaw"
==> Checking that code complies with gofmt requirements...
go generate $(go list ./... | grep -v /terraform/vendor/)
2017/08/02 11:17:32 Generated command/internal_plugin_list.go
go test -i ./helper/schema || exit 1
echo ./helper/schema | \
        xargs -t -n4 go test -run=TestResourceDataGetOkRaw -timeout=60s -parallel=4
go test -run=TestResourceDataGetOkRaw -timeout=60s -parallel=4 ./helper/schema
ok      github.com/hashicorp/terraform/helper/schema    0.005s
```
},

{
Name: "bool-literal-empty",
Copy link
Member

Choose a reason for hiding this comment

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

I think this is also one of the troublesome combinations, and we need the the test to look like string-literal-empty above, except with TypeBool. That will at least give us one other data point where GetOkExists differs from GetOk.

},
}

for _, tc := range cases {
Copy link
Member

Choose a reason for hiding this comment

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

Any new table-driven tests should be using subtests. As a rule of thumb, any old test that I touch gets converted too. This makes debugging changes much easier, since you can see all the case failures at once, rather than one at a time.

@grubernaut
Copy link
Contributor Author

Updated:

$ make test TEST=./helper/schema TESTARGS="-v -run=TestResourceDataGetOkExists"                                                                                                              
==> Checking that code complies with gofmt requirements...                                                                                                                                   
go generate $(go list ./... | grep -v /terraform/vendor/)                                                                                                                                    
2017/08/03 17:52:50 Generated command/internal_plugin_list.go                                                                                                                                
go test -i ./helper/schema || exit 1                                                                                                                                                         
echo ./helper/schema | \                                                                                                                                                                     
        xargs -t -n4 go test -v -run=TestResourceDataGetOkExists -timeout=60s -parallel=4                                                                                                    
go test -v -run=TestResourceDataGetOkExists -timeout=60s -parallel=4 ./helper/schema                                                                                                         
=== RUN   TestResourceDataGetOkExists                                                                                                                                                        
=== RUN   TestResourceDataGetOkExists/0-string-literal-empty                                                                                                                                 
=== RUN   TestResourceDataGetOkExists/1-string-computed-empty                                                                                                                                
=== RUN   TestResourceDataGetOkExists/2-string-optional-computed-nil-diff                                                                                                                    
=== RUN   TestResourceDataGetOkExists/3-list-optional                                                                                                                                        
=== RUN   TestResourceDataGetOkExists/4-map-optional                                                                                                                                         
=== RUN   TestResourceDataGetOkExists/5-set-optional                                                                                                                                         
=== RUN   TestResourceDataGetOkExists/6-set-optional-key                                                                                                                                     
=== RUN   TestResourceDataGetOkExists/7-bool-literal-empty                                                                                                                                   
=== RUN   TestResourceDataGetOkExists/8-bool-literal-set
--- PASS: TestResourceDataGetOkExists (0.00s)
    --- PASS: TestResourceDataGetOkExists/0-string-literal-empty (0.00s)
    --- PASS: TestResourceDataGetOkExists/1-string-computed-empty (0.00s)
    --- PASS: TestResourceDataGetOkExists/2-string-optional-computed-nil-diff (0.00s)
    --- PASS: TestResourceDataGetOkExists/3-list-optional (0.00s)
    --- PASS: TestResourceDataGetOkExists/4-map-optional (0.00s)
    --- PASS: TestResourceDataGetOkExists/5-set-optional (0.00s)
    --- PASS: TestResourceDataGetOkExists/6-set-optional-key (0.00s)
    --- PASS: TestResourceDataGetOkExists/7-bool-literal-empty (0.00s)
    --- PASS: TestResourceDataGetOkExists/8-bool-literal-set (0.00s)
PASS
ok      github.com/hashicorp/terraform/helper/schema    0.005s

Copy link
Member

@jbardin jbardin left a comment

Choose a reason for hiding this comment

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

LGTM!

@jen20
Copy link
Contributor

jen20 commented Aug 4, 2017

👍 This has been a thorn for provider writers for a long time - happy to see a straightforward fix!

grubernaut added a commit to hashicorp/terraform-provider-aws that referenced this pull request Aug 4, 2017
Previously we had no way of determining if a boolean attribute was a
literal `false` or a zero-value type of `false`. With the core enhancement
added to Terraform via hashicorp/terraform#15723,
we can now reliably check for a zero-type value of a boolean attribute,
or a literal boolean `false` inside of schema.

This allows the `associate_public_ip_address` to work as expected,
when configuring an AWS Instance inside of a subnet that defaults to
public address assignment, with a literal `false` for the `associate_public_ip_address` attribute.

```
$ make testacc TEST=./aws TESTARGS="-run=TestAccAWSInstance_explicitAssociatePublicAddress"
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws -v -run=TestAccAWSInstance_explicitAssociatePublicAddress -timeout 120m
=== RUN   TestAccAWSInstance_explicitAssociatePublicAddress
--- PASS: TestAccAWSInstance_explicitAssociatePublicAddress (113.04s)
PASS
ok      github.com/terraform-providers/terraform-provider-aws/aws       113.045s
```
@apparentlymart
Copy link
Member

apparentlymart commented Aug 8, 2017

This looks similar to what was discussed in #14139; note that I found a troublesome case there with an attribute that was already in state and then removed from config. I suspect (though didn't confirm) that this change would have the same limitation, but perhaps we can accept that edge case given that this should work as expected in the main case.

We'll just need to be on the look out for bugs of this sort:

  • An attribute is TypeBool with a default of true.
  • Resource is created with the value explicitly set to false.
  • User removes the argument from config altogether, expecting it to adopt the default of true in the next plan.
  • ...but actually, it remains false because GetOkExists returned false, true.

@jen20
Copy link
Contributor

jen20 commented Aug 8, 2017

That's actually the same pathology I found when I implemented this a while back (never committed, obviously). I think it should be easy to reproduce in a test to verify - fortunately it should be clear from a plan that the default has not been adopted.

@grubernaut
Copy link
Contributor Author

Hey @jen20 + @apparentlymart,

I was able to test the scenario listed above with a resource that was TypeBool and the default of true. Creating the resource with an explicit false works as expected, and removing the attribute from config caused the plan to pick up the correct Default value.

-/+ aws_instance.web (new resource required)
      jake_test_attribute:  "false" => "true" (forces new resource)

Working on translating this into an accurate unit test at the moment.

grubernaut added a commit to hashicorp/terraform-provider-aws that referenced this pull request Aug 8, 2017
Previously we had no way of determining if a boolean attribute was a
literal `false` or a zero-value type of `false`. With the core enhancement
added to Terraform via hashicorp/terraform#15723,
we can now reliably check for a zero-type value of a boolean attribute,
or a literal boolean `false` inside of schema.

This allows the `associate_public_ip_address` to work as expected,
when configuring an AWS Instance inside of a subnet that defaults to
public address assignment, with a literal `false` for the `associate_public_ip_address` attribute.

```
$ make testacc TEST=./aws TESTARGS="-run=TestAccAWSInstance_associatePublic_"
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws -v -run=TestAccAWSInstance_associatePublic_ -timeout 120m
=== RUN   TestAccAWSInstance_associatePublic_defaultPrivate
--- PASS: TestAccAWSInstance_associatePublic_defaultPrivate (106.54s)
=== RUN   TestAccAWSInstance_associatePublic_defaultPublic
--- PASS: TestAccAWSInstance_associatePublic_defaultPublic (215.38s)
=== RUN   TestAccAWSInstance_associatePublic_explicitPublic
--- PASS: TestAccAWSInstance_associatePublic_explicitPublic (112.23s)
=== RUN   TestAccAWSInstance_associatePublic_explicitPrivate
--- PASS: TestAccAWSInstance_associatePublic_explicitPrivate (224.96s)
=== RUN   TestAccAWSInstance_associatePublic_overridePublic
--- PASS: TestAccAWSInstance_associatePublic_overridePublic (117.78s)
=== RUN   TestAccAWSInstance_associatePublic_overridePrivate
--- PASS: TestAccAWSInstance_associatePublic_overridePrivate (112.48s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	889.375s
```
@tmshn
Copy link
Contributor

tmshn commented Aug 14, 2017

So the problem is totally fixed?

As I looked through, the code of the function is same as what I wrote here.
So I'm worried about that the same problem might happen which @apparentlymart pointed out here:
#14139 (comment)

nbaztec pushed a commit to nbaztec/terraform-provider-aws that referenced this pull request Sep 26, 2017
Previously we had no way of determining if a boolean attribute was a
literal `false` or a zero-value type of `false`. With the core enhancement
added to Terraform via hashicorp/terraform#15723,
we can now reliably check for a zero-type value of a boolean attribute,
or a literal boolean `false` inside of schema.

This allows the `associate_public_ip_address` to work as expected,
when configuring an AWS Instance inside of a subnet that defaults to
public address assignment, with a literal `false` for the `associate_public_ip_address` attribute.

```
$ make testacc TEST=./aws TESTARGS="-run=TestAccAWSInstance_associatePublic_"
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws -v -run=TestAccAWSInstance_associatePublic_ -timeout 120m
=== RUN   TestAccAWSInstance_associatePublic_defaultPrivate
--- PASS: TestAccAWSInstance_associatePublic_defaultPrivate (106.54s)
=== RUN   TestAccAWSInstance_associatePublic_defaultPublic
--- PASS: TestAccAWSInstance_associatePublic_defaultPublic (215.38s)
=== RUN   TestAccAWSInstance_associatePublic_explicitPublic
--- PASS: TestAccAWSInstance_associatePublic_explicitPublic (112.23s)
=== RUN   TestAccAWSInstance_associatePublic_explicitPrivate
--- PASS: TestAccAWSInstance_associatePublic_explicitPrivate (224.96s)
=== RUN   TestAccAWSInstance_associatePublic_overridePublic
--- PASS: TestAccAWSInstance_associatePublic_overridePublic (117.78s)
=== RUN   TestAccAWSInstance_associatePublic_overridePrivate
--- PASS: TestAccAWSInstance_associatePublic_overridePrivate (112.48s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	889.375s
```
hypnoglow added a commit to hypnoglow/terraform-provider-gitlab that referenced this pull request Oct 16, 2018
GitLab Slack service implementation requires terraform's
ResourceData.GetOkExists method, because of the large number of boolean
attributes. See: hashicorp/terraform#15723
hypnoglow added a commit to hypnoglow/terraform-provider-gitlab that referenced this pull request Oct 16, 2018
GitLab Slack service implementation requires terraform's
ResourceData.GetOkExists method, because of the large number of boolean
attributes. See: hashicorp/terraform#15723
@ghost
Copy link

ghost commented Apr 7, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Apr 7, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants