793 lines
16 KiB
Go
793 lines
16 KiB
Go
package test
|
|
|
|
import (
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckNoResourceAttr(
|
|
"test_resource.foo", "list.#",
|
|
),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_changedList(t *testing.T) {
|
|
resource.UnitTest(t, resource.TestCase{
|
|
Providers: testAccProviders,
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckNoResourceAttr(
|
|
"test_resource.foo", "list.#",
|
|
),
|
|
),
|
|
},
|
|
{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
list = ["a"]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.#", "1",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.0", "a",
|
|
),
|
|
),
|
|
},
|
|
{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
list = ["a", "b"]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.#", "2",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.0", "a",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.1", "b",
|
|
),
|
|
),
|
|
},
|
|
{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
list = ["b"]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.#", "1",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
"test_resource.foo", "list.0", "b",
|
|
),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// 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" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
lifecycle {
|
|
ignore_changes = ["required"]
|
|
}
|
|
}
|
|
`),
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "two"
|
|
lifecycle {
|
|
ignore_changes = ["optional_force_new"]
|
|
}
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// 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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "one"
|
|
optional_bool = true
|
|
lifecycle {
|
|
ignore_changes = ["optional_force_new"]
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "two"
|
|
optional_bool = true
|
|
lifecycle {
|
|
ignore_changes = ["optional_force_new"]
|
|
}
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_computed_map = {
|
|
foo = "bar"
|
|
no = "update"
|
|
}
|
|
lifecycle {
|
|
ignore_changes = ["optional_computed_map"]
|
|
}
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_ignoreChangesDependent(t *testing.T) {
|
|
resource.UnitTest(t, resource.TestCase{
|
|
Providers: testAccProviders,
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
Steps: []resource.TestStep{
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
count = 2
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
|
|
optional_force_new = "one"
|
|
lifecycle {
|
|
ignore_changes = ["optional_force_new"]
|
|
}
|
|
}
|
|
resource "test_resource" "bar" {
|
|
count = 2
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional = "${element(test_resource.foo.*.id, count.index)}"
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
count = 2
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
|
|
optional_force_new = "two"
|
|
lifecycle {
|
|
ignore_changes = ["optional_force_new"]
|
|
}
|
|
}
|
|
resource "test_resource" "bar" {
|
|
count = 2
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional = "${element(test_resource.foo.*.id, count.index)}"
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_ignoreChangesStillReplaced(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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "one"
|
|
optional_bool = true
|
|
lifecycle {
|
|
ignore_changes = ["optional_bool"]
|
|
}
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "two"
|
|
optional_bool = false
|
|
lifecycle {
|
|
ignore_changes = ["optional_bool"]
|
|
}
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Reproduces plan-time panic when the wrong type is interpolated in a list of
|
|
// maps.
|
|
// TODO: this should return a type error, rather than silently setting an empty
|
|
// list
|
|
func TestResource_dataSourceListMapPanic(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 = "val"
|
|
required_map = {x = "y"}
|
|
list_of_map = "${var.maplist}"
|
|
}
|
|
|
|
variable "maplist" {
|
|
type = "list"
|
|
|
|
default = [
|
|
{a = "b"}
|
|
]
|
|
}
|
|
`),
|
|
ExpectError: nil,
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_dataSourceIndexMapList(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 = "val"
|
|
|
|
required_map = {
|
|
x = "y"
|
|
}
|
|
|
|
list_of_map = [
|
|
{
|
|
a = "1"
|
|
b = "2"
|
|
},
|
|
{
|
|
c = "3"
|
|
d = "4"
|
|
},
|
|
]
|
|
}
|
|
|
|
output "map_from_list" {
|
|
value = "${test_resource.foo.list_of_map[0]}"
|
|
}
|
|
|
|
output "value_from_map_from_list" {
|
|
value = "${lookup(test_resource.foo.list_of_map[1], "d")}"
|
|
}
|
|
`),
|
|
ExpectError: nil,
|
|
Check: func(s *terraform.State) error {
|
|
root := s.ModuleByPath(addrs.RootModuleInstance)
|
|
mapOut := root.Outputs["map_from_list"].Value
|
|
expectedMapOut := map[string]interface{}{
|
|
"a": "1",
|
|
"b": "2",
|
|
}
|
|
|
|
valueOut := root.Outputs["value_from_map_from_list"].Value
|
|
expectedValueOut := "4"
|
|
|
|
if !reflect.DeepEqual(mapOut, expectedMapOut) {
|
|
t.Fatalf("Expected: %#v\nGot: %#v", expectedMapOut, mapOut)
|
|
}
|
|
if !reflect.DeepEqual(valueOut, expectedValueOut) {
|
|
t.Fatalf("Expected: %#v\nGot: %#v", valueOut, expectedValueOut)
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func testAccCheckResourceDestroy(s *terraform.State) error {
|
|
return nil
|
|
}
|
|
|
|
func TestResource_removeForceNew(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"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
optional_force_new = "here"
|
|
}
|
|
`),
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "yep"
|
|
required_map = {
|
|
key = "value"
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_unknownFuncInMap(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 = "${uuid()}"
|
|
}
|
|
}
|
|
`),
|
|
ExpectNonEmptyPlan: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Verify that we can destroy when a managed resource references something with
|
|
// a count of 1.
|
|
func TestResource_countRefDestroyError(t *testing.T) {
|
|
resource.UnitTest(t, resource.TestCase{
|
|
Providers: testAccProviders,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "one" {
|
|
count = 1
|
|
required = "ok"
|
|
required_map = {
|
|
key = "val"
|
|
}
|
|
}
|
|
|
|
resource "test_resource" "two" {
|
|
required = test_resource.one[0].id
|
|
required_map = {
|
|
key = "val"
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_emptyMapValue(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 = {
|
|
a = "a"
|
|
b = ""
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_updateError(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 = "first"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
apply_error = "update_error"
|
|
}
|
|
`),
|
|
ExpectError: regexp.MustCompile("update_error"),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_applyError(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 = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
apply_error = "apply_error"
|
|
}
|
|
`),
|
|
ExpectError: regexp.MustCompile("apply_error"),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_emptyStrings(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 = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
|
|
list = [""]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""),
|
|
),
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
|
|
list = ["", "b"]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""),
|
|
resource.TestCheckResourceAttr("test_resource.foo", "list.1", "b"),
|
|
),
|
|
},
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
|
|
list = [""]
|
|
}
|
|
`),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestResource_setDrift(t *testing.T) {
|
|
testProvider := testAccProviders["test"]
|
|
res := testProvider.(*schema.Provider).ResourcesMap["test_resource"]
|
|
|
|
// reset the Read function after the test
|
|
defer func() {
|
|
res.Read = testResourceRead
|
|
}()
|
|
|
|
resource.UnitTest(t, resource.TestCase{
|
|
Providers: testAccProviders,
|
|
CheckDestroy: testAccCheckResourceDestroy,
|
|
Steps: []resource.TestStep{
|
|
resource.TestStep{
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "first"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
set = ["a", "b"]
|
|
}
|
|
`),
|
|
Check: func(s *terraform.State) error {
|
|
return nil
|
|
},
|
|
},
|
|
resource.TestStep{
|
|
PreConfig: func() {
|
|
// update the Read function to return the wrong "set" attribute values.
|
|
res.Read = func(d *schema.ResourceData, meta interface{}) error {
|
|
// update as expected first
|
|
if err := testResourceRead(d, meta); err != nil {
|
|
return err
|
|
}
|
|
d.Set("set", []interface{}{"a", "x"})
|
|
return nil
|
|
}
|
|
},
|
|
// Leave the config, so we can detect the mismatched set values.
|
|
// Updating the config would force the test to pass even if the Read
|
|
// function values were ignored.
|
|
Config: strings.TrimSpace(`
|
|
resource "test_resource" "foo" {
|
|
required = "second"
|
|
required_map = {
|
|
a = "a"
|
|
}
|
|
set = ["a", "b"]
|
|
}
|
|
`),
|
|
ExpectNonEmptyPlan: true,
|
|
},
|
|
},
|
|
})
|
|
}
|