2016-03-15 16:11:28 +01:00
|
|
|
package test
|
|
|
|
|
|
|
|
import (
|
2016-07-01 01:22:20 +02:00
|
|
|
"regexp"
|
2016-03-15 16:11:28 +01:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestResource_basic(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:11:28 +01:00
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-03-15 16:03:01 +01:00
|
|
|
// Targeted test in TestContext2Apply_ignoreChangesCreate
|
|
|
|
func TestResource_ignoreChangesRequired(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
2016-06-05 11:04:08 +02:00
|
|
|
required = "yep"
|
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["required"]
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResource_ignoreChangesEmpty(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_force_new = "one"
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-07-01 01:22:20 +02:00
|
|
|
required_map {
|
2016-06-05 11:04:08 +02:00
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_force_new = "two"
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResource_ignoreChangesForceNew(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-07-01 01:22:20 +02:00
|
|
|
required_map {
|
2016-06-05 11:04:08 +02:00
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_force_new = "one"
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_force_new"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_force_new = "two"
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_force_new"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
helper/schema: Normalize bools to "true"/"false" in diffs
For a long time now, the diff logic has relied on the behavior of
`mapstructure.WeakDecode` to determine how various primitives are
converted into strings. The `schema.DiffString` function is used for
all primitive field types: TypeBool, TypeInt, TypeFloat, and TypeString.
The `mapstructure` library's string representation of booleans is "0"
and "1", which differs from `strconv.FormatBool`'s "false" and "true"
(which is used in writing out boolean fields to the state).
Because of this difference, diffs have long had the potential for
cosmetically odd but semantically neutral output like:
"true" => "1"
"false" => "0"
So long as `mapstructure.Decode` or `strconv.ParseBool` are used to
interpret these strings, there's no functional problem.
We had our first clear functional problem with #6005 and friends, where
users noticed diffs like the above showing up unexpectedly and causing
troubles when `ignore_changes` was in play.
This particular bug occurs down in Terraform core's EvalIgnoreChanges.
There, the diff is modified to account for ignored attributes, and
special logic attempts to handle properly the situation where the
ignored attribute was going to trigger a resource replacement. That
logic relies on the string representations of the Old and New fields in
the diff to be the same so that it filters properly.
So therefore, we now get a bug when a diff includes `Old: "0", New:
"false"` since the strings do not match, and `ignore_changes` is not
properly handled.
Here, we introduce `TypeBool`-specific normalizing into `finalizeDiff`.
I spiked out a full `diffBool` function, but figuring out which pieces
of `diffString` to duplicate there got hairy. This seemed like a simpler
and more direct solution.
Fixes #6005 (and potentially others!)
2016-05-05 16:00:58 +02:00
|
|
|
// Covers specific scenario in #6005, handled by normalizing boolean strings in
|
|
|
|
// helper/schema
|
|
|
|
func TestResource_ignoreChangesForceNewBoolean(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
helper/schema: Normalize bools to "true"/"false" in diffs
For a long time now, the diff logic has relied on the behavior of
`mapstructure.WeakDecode` to determine how various primitives are
converted into strings. The `schema.DiffString` function is used for
all primitive field types: TypeBool, TypeInt, TypeFloat, and TypeString.
The `mapstructure` library's string representation of booleans is "0"
and "1", which differs from `strconv.FormatBool`'s "false" and "true"
(which is used in writing out boolean fields to the state).
Because of this difference, diffs have long had the potential for
cosmetically odd but semantically neutral output like:
"true" => "1"
"false" => "0"
So long as `mapstructure.Decode` or `strconv.ParseBool` are used to
interpret these strings, there's no functional problem.
We had our first clear functional problem with #6005 and friends, where
users noticed diffs like the above showing up unexpectedly and causing
troubles when `ignore_changes` was in play.
This particular bug occurs down in Terraform core's EvalIgnoreChanges.
There, the diff is modified to account for ignored attributes, and
special logic attempts to handle properly the situation where the
ignored attribute was going to trigger a resource replacement. That
logic relies on the string representations of the Old and New fields in
the diff to be the same so that it filters properly.
So therefore, we now get a bug when a diff includes `Old: "0", New:
"false"` since the strings do not match, and `ignore_changes` is not
properly handled.
Here, we introduce `TypeBool`-specific normalizing into `finalizeDiff`.
I spiked out a full `diffBool` function, but figuring out which pieces
of `diffString` to duplicate there got hairy. This seemed like a simpler
and more direct solution.
Fixes #6005 (and potentially others!)
2016-05-05 16:00:58 +02:00
|
|
|
optional_force_new = "one"
|
|
|
|
optional_bool = true
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_force_new"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
helper/schema: Normalize bools to "true"/"false" in diffs
For a long time now, the diff logic has relied on the behavior of
`mapstructure.WeakDecode` to determine how various primitives are
converted into strings. The `schema.DiffString` function is used for
all primitive field types: TypeBool, TypeInt, TypeFloat, and TypeString.
The `mapstructure` library's string representation of booleans is "0"
and "1", which differs from `strconv.FormatBool`'s "false" and "true"
(which is used in writing out boolean fields to the state).
Because of this difference, diffs have long had the potential for
cosmetically odd but semantically neutral output like:
"true" => "1"
"false" => "0"
So long as `mapstructure.Decode` or `strconv.ParseBool` are used to
interpret these strings, there's no functional problem.
We had our first clear functional problem with #6005 and friends, where
users noticed diffs like the above showing up unexpectedly and causing
troubles when `ignore_changes` was in play.
This particular bug occurs down in Terraform core's EvalIgnoreChanges.
There, the diff is modified to account for ignored attributes, and
special logic attempts to handle properly the situation where the
ignored attribute was going to trigger a resource replacement. That
logic relies on the string representations of the Old and New fields in
the diff to be the same so that it filters properly.
So therefore, we now get a bug when a diff includes `Old: "0", New:
"false"` since the strings do not match, and `ignore_changes` is not
properly handled.
Here, we introduce `TypeBool`-specific normalizing into `finalizeDiff`.
I spiked out a full `diffBool` function, but figuring out which pieces
of `diffString` to duplicate there got hairy. This seemed like a simpler
and more direct solution.
Fixes #6005 (and potentially others!)
2016-05-05 16:00:58 +02:00
|
|
|
optional_force_new = "two"
|
|
|
|
optional_bool = true
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_force_new"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
2016-07-01 01:22:20 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reproduces plan-time panic described in GH-7170
|
|
|
|
func TestResource_dataSourceListPlanPanic(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
data "test_data_source" "foo" {}
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "${data.test_data_source.foo.list}"
|
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
ExpectError: regexp.MustCompile(`must be a single value, not a list`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reproduces apply-time panic described in GH-7170
|
|
|
|
func TestResource_dataSourceListApplyPanic(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "ok"
|
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resource "test_resource" "bar" {
|
|
|
|
required = "${test_resource.foo.computed_list}"
|
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
ExpectError: regexp.MustCompile(`must be a single value, not a list`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
helper/schema: Normalize bools to "true"/"false" in diffs
For a long time now, the diff logic has relied on the behavior of
`mapstructure.WeakDecode` to determine how various primitives are
converted into strings. The `schema.DiffString` function is used for
all primitive field types: TypeBool, TypeInt, TypeFloat, and TypeString.
The `mapstructure` library's string representation of booleans is "0"
and "1", which differs from `strconv.FormatBool`'s "false" and "true"
(which is used in writing out boolean fields to the state).
Because of this difference, diffs have long had the potential for
cosmetically odd but semantically neutral output like:
"true" => "1"
"false" => "0"
So long as `mapstructure.Decode` or `strconv.ParseBool` are used to
interpret these strings, there's no functional problem.
We had our first clear functional problem with #6005 and friends, where
users noticed diffs like the above showing up unexpectedly and causing
troubles when `ignore_changes` was in play.
This particular bug occurs down in Terraform core's EvalIgnoreChanges.
There, the diff is modified to account for ignored attributes, and
special logic attempts to handle properly the situation where the
ignored attribute was going to trigger a resource replacement. That
logic relies on the string representations of the Old and New fields in
the diff to be the same so that it filters properly.
So therefore, we now get a bug when a diff includes `Old: "0", New:
"false"` since the strings do not match, and `ignore_changes` is not
properly handled.
Here, we introduce `TypeBool`-specific normalizing into `finalizeDiff`.
I spiked out a full `diffBool` function, but figuring out which pieces
of `diffString` to duplicate there got hairy. This seemed like a simpler
and more direct solution.
Fixes #6005 (and potentially others!)
2016-05-05 16:00:58 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-03-15 16:03:01 +01:00
|
|
|
func TestResource_ignoreChangesMap(t *testing.T) {
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_computed_map {
|
|
|
|
foo = "bar"
|
|
|
|
}
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_computed_map"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
resource.TestStep{
|
|
|
|
Config: strings.TrimSpace(`
|
|
|
|
resource "test_resource" "foo" {
|
|
|
|
required = "yep"
|
2016-06-05 11:04:08 +02:00
|
|
|
required_map = {
|
|
|
|
key = "value"
|
|
|
|
}
|
2016-03-15 16:03:01 +01:00
|
|
|
optional_computed_map {
|
|
|
|
foo = "bar"
|
|
|
|
no = "update"
|
|
|
|
}
|
|
|
|
lifecycle {
|
|
|
|
ignore_changes = ["optional_computed_map"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
Check: func(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-03-15 16:11:28 +01:00
|
|
|
func testAccCheckResourceDestroy(s *terraform.State) error {
|
|
|
|
return nil
|
|
|
|
}
|