Merge pull request #26961 from hashicorp/jbardin/internal-legacy-packages
Move deprecated packages to internal/legacy
This commit is contained in:
commit
381cc0b5d6
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/states/remote"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
artifactory "github.com/lusis/go-artifactory/src/artifactory.v401"
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
// New creates a new backend for Azure remote state.
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
// New creates a new backend for Consul remote state.
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813"
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
etcdapi "github.com/coreos/etcd/client"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/states/remote"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
etcdv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
|
||||
"cloud.google.com/go/storage"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/httpclient"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
"google.golang.org/api/option"
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/states/remote"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
statespkg "github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/states/remote"
|
||||
"github.com/hashicorp/terraform/states/statemgr"
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
triton "github.com/joyent/triton-go"
|
||||
"github.com/joyent/triton-go/authentication"
|
||||
"github.com/joyent/triton-go/storage"
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
"github.com/jmespath/go-jmespath"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
awsbase "github.com/hashicorp/aws-sdk-go-base"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/logging"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/gophercloud/utils/terraform/auth"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestProviderLabelDataSource(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
provider "test" {
|
||||
label = "foo"
|
||||
}
|
||||
|
||||
data "test_provider_label" "test" {
|
||||
}
|
||||
`),
|
||||
Check: func(s *terraform.State) error {
|
||||
res, hasRes := s.RootModule().Resources["data.test_provider_label.test"]
|
||||
if !hasRes {
|
||||
return errors.New("No test_provider_label in state")
|
||||
}
|
||||
if got, want := res.Primary.ID, "foo"; got != want {
|
||||
return fmt.Errorf("wrong id %q; want %q", got, want)
|
||||
}
|
||||
if got, want := res.Primary.Attributes["label"], "foo"; got != want {
|
||||
return fmt.Errorf("wrong id %q; want %q", got, want)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestDataSource_dataSourceCount(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
data "test_data_source" "test" {
|
||||
count = 3
|
||||
input = "count-${count.index}"
|
||||
}
|
||||
|
||||
resource "test_resource" "foo" {
|
||||
required = "yep"
|
||||
required_map = {
|
||||
key = "value"
|
||||
}
|
||||
|
||||
list = "${data.test_data_source.test.*.output}"
|
||||
}
|
||||
`),
|
||||
Check: func(s *terraform.State) error {
|
||||
res, hasRes := s.RootModule().Resources["test_resource.foo"]
|
||||
if !hasRes {
|
||||
return errors.New("No test_resource.foo in state")
|
||||
}
|
||||
if res.Primary.Attributes["list.#"] != "3" {
|
||||
return errors.New("Wrong list.#, expected 3")
|
||||
}
|
||||
if res.Primary.Attributes["list.0"] != "count-0" {
|
||||
return errors.New("Wrong list.0, expected count-0")
|
||||
}
|
||||
if res.Primary.Attributes["list.1"] != "count-1" {
|
||||
return errors.New("Wrong list.0, expected count-1")
|
||||
}
|
||||
if res.Primary.Attributes["list.2"] != "count-2" {
|
||||
return errors.New("Wrong list.0, expected count-2")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test that the output of a data source can be used as the value for
|
||||
// a "count" in a real resource. This would fail with "count cannot be computed"
|
||||
// at some point.
|
||||
func TestDataSource_valueAsResourceCount(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
data "test_data_source" "test" {
|
||||
input = "4"
|
||||
}
|
||||
|
||||
resource "test_resource" "foo" {
|
||||
count = "${data.test_data_source.test.output}"
|
||||
|
||||
required = "yep"
|
||||
required_map = {
|
||||
key = "value"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: func(s *terraform.State) error {
|
||||
count := 0
|
||||
for k, _ := range s.RootModule().Resources {
|
||||
if strings.HasPrefix(k, "test_resource.foo.") {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
if count != 4 {
|
||||
return fmt.Errorf("bad count: %d", count)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestDataSource_dataSourceCountGrandChild tests that a grandchild data source
|
||||
// that is based off of count works, ie: dependency chain foo -> bar -> baz.
|
||||
// This was failing because CountBoundaryTransformer is being run during apply
|
||||
// instead of plan, which meant that it wasn't firing after data sources were
|
||||
// potentially changing state and causing diff/interpolation issues.
|
||||
//
|
||||
// This happens after the initial apply, after state is saved.
|
||||
func TestDataSource_dataSourceCountGrandChild(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: dataSourceCountGrandChildConfig,
|
||||
},
|
||||
{
|
||||
Config: dataSourceCountGrandChildConfig,
|
||||
Check: func(s *terraform.State) error {
|
||||
for _, v := range []string{"foo", "bar", "baz"} {
|
||||
count := 0
|
||||
for k := range s.RootModule().Resources {
|
||||
if strings.HasPrefix(k, fmt.Sprintf("data.test_data_source.%s.", v)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
if count != 2 {
|
||||
return fmt.Errorf("bad count for data.test_data_source.%s: %d", v, count)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const dataSourceCountGrandChildConfig = `
|
||||
data "test_data_source" "foo" {
|
||||
count = 2
|
||||
input = "one"
|
||||
}
|
||||
|
||||
data "test_data_source" "bar" {
|
||||
count = "${length(data.test_data_source.foo.*.id)}"
|
||||
input = "${data.test_data_source.foo.*.output[count.index]}"
|
||||
}
|
||||
|
||||
data "test_data_source" "baz" {
|
||||
count = "${length(data.test_data_source.bar.*.id)}"
|
||||
input = "${data.test_data_source.bar.*.output[count.index]}"
|
||||
}
|
||||
`
|
||||
|
||||
func TestDataSource_nilComputedValues(t *testing.T) {
|
||||
check := func(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Check: check,
|
||||
Config: `
|
||||
variable "index" {
|
||||
default = "d"
|
||||
}
|
||||
|
||||
locals {
|
||||
name = {
|
||||
a = "something"
|
||||
b = "else"
|
||||
}
|
||||
}
|
||||
|
||||
data "test_data_source" "x" {
|
||||
input = "${lookup(local.name, var.index, local.name["a"])}"
|
||||
}
|
||||
|
||||
data "test_data_source" "y" {
|
||||
input = data.test_data_source.x.nil == "something" ? "something" : "else"
|
||||
}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// referencing test_data_source.one.output_map["a"] should produce an error when
|
||||
// there's a count.
|
||||
func TestDataSource_indexedCountOfOne(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
data "test_data_source" "one" {
|
||||
count = 1
|
||||
input_map = {
|
||||
"a" = "b"
|
||||
}
|
||||
}
|
||||
|
||||
data "test_data_source" "two" {
|
||||
input_map = {
|
||||
"x" = data.test_data_source.one.output_map["a"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("Because data.test_data_source.one has \"count\" set, its attributes must be accessed on specific instances"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Verify that we can destroy when a data source references something with a
|
||||
// count of 1.
|
||||
func TestDataSource_countRefDestroyError(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
data "test_data_source" "one" {
|
||||
count = 1
|
||||
input = "a"
|
||||
}
|
||||
|
||||
data "test_data_source" "two" {
|
||||
input = data.test_data_source.one[0].output
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDataSource_planUpdate(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource" "a" {
|
||||
required = "first"
|
||||
required_map = {
|
||||
key = "1"
|
||||
}
|
||||
optional_force_new = "first"
|
||||
}
|
||||
|
||||
data "test_data_source" "a" {
|
||||
input = "${test_resource.a.computed_from_required}"
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = "${data.test_data_source.a.output}"
|
||||
}
|
||||
`),
|
||||
},
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource" "a" {
|
||||
required = "second"
|
||||
required_map = {
|
||||
key = "1"
|
||||
}
|
||||
optional_force_new = "second"
|
||||
}
|
||||
|
||||
data "test_data_source" "a" {
|
||||
input = "${test_resource.a.computed_from_required}"
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = "${data.test_data_source.a.output}"
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.test_data_source.a", "output", "second"),
|
||||
resource.TestCheckOutput("out", "second"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestDiffApply_set(t *testing.T) {
|
||||
priorAttrs := map[string]string{
|
||||
"id": "testID",
|
||||
"egress.#": "1",
|
||||
"egress.2129912301.cidr_blocks.#": "1",
|
||||
"egress.2129912301.cidr_blocks.0": "10.0.0.0/8",
|
||||
"egress.2129912301.description": "Egress description",
|
||||
"egress.2129912301.from_port": "80",
|
||||
"egress.2129912301.ipv6_cidr_blocks.#": "0",
|
||||
"egress.2129912301.prefix_list_ids.#": "0",
|
||||
"egress.2129912301.protocol": "tcp",
|
||||
"egress.2129912301.security_groups.#": "0",
|
||||
"egress.2129912301.self": "false",
|
||||
"egress.2129912301.to_port": "8000",
|
||||
}
|
||||
|
||||
diff := &terraform.InstanceDiff{
|
||||
Attributes: map[string]*terraform.ResourceAttrDiff{
|
||||
"egress.2129912301.cidr_blocks.#": {Old: "1", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.2129912301.cidr_blocks.0": {Old: "10.0.0.0/8", New: "", NewComputed: false, NewRemoved: true},
|
||||
"egress.2129912301.description": {Old: "Egress description", New: "", NewComputed: false, NewRemoved: true},
|
||||
"egress.2129912301.from_port": {Old: "80", New: "0", NewComputed: false, NewRemoved: true},
|
||||
"egress.2129912301.ipv6_cidr_blocks.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.2129912301.prefix_list_ids.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.2129912301.protocol": {Old: "tcp", New: "", NewComputed: false, NewRemoved: true},
|
||||
"egress.2129912301.security_groups.#": {Old: "0", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.2129912301.self": {Old: "false", New: "false", NewComputed: false, NewRemoved: true},
|
||||
"egress.2129912301.to_port": {Old: "8000", New: "0", NewComputed: false, NewRemoved: true},
|
||||
"egress.746197026.cidr_blocks.#": {Old: "", New: "1", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.cidr_blocks.0": {Old: "", New: "10.0.0.0/8", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.description": {Old: "", New: "New egress description", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.from_port": {Old: "", New: "80", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.ipv6_cidr_blocks.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.prefix_list_ids.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.protocol": {Old: "", New: "tcp", NewComputed: false, NewRemoved: false, NewExtra: "tcp"},
|
||||
"egress.746197026.security_groups.#": {Old: "", New: "0", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.self": {Old: "", New: "false", NewComputed: false, NewRemoved: false},
|
||||
"egress.746197026.to_port": {Old: "", New: "8000", NewComputed: false, NewRemoved: false},
|
||||
// an erroneous nil diff should do nothing
|
||||
"egress.111111111.to_port": nil,
|
||||
},
|
||||
}
|
||||
|
||||
resSchema := map[string]*schema.Schema{
|
||||
"egress": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ConfigMode: schema.SchemaConfigModeAttr,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"from_port": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"to_port": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"protocol": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"cidr_blocks": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
|
||||
"ipv6_cidr_blocks": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
|
||||
"prefix_list_ids": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
"security_groups": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
|
||||
"self": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := map[string]string{
|
||||
"egress.#": "1",
|
||||
"egress.746197026.cidr_blocks.#": "1",
|
||||
"egress.746197026.cidr_blocks.0": "10.0.0.0/8",
|
||||
"egress.746197026.description": "New egress description",
|
||||
"egress.746197026.from_port": "80", "egress.746197026.ipv6_cidr_blocks.#": "0",
|
||||
"egress.746197026.prefix_list_ids.#": "0",
|
||||
"egress.746197026.protocol": "tcp",
|
||||
"egress.746197026.security_groups.#": "0",
|
||||
"egress.746197026.self": "false",
|
||||
"egress.746197026.to_port": "8000",
|
||||
"id": "testID",
|
||||
}
|
||||
|
||||
attrs, err := diff.Apply(priorAttrs, (&schema.Resource{Schema: resSchema}).CoreConfigSchema())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(attrs, expected) {
|
||||
t.Fatalf("wrong result\ngot: %s\nwant: %s\n", spew.Sdump(attrs), spew.Sdump(expected))
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"test": testAccProvider,
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceComputedSet_update(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_computed_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_computed_set.foo", "string_set.#", "3",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_computed_set" "foo" {
|
||||
set_count = 5
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_computed_set.foo", "string_set.#", "5",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_computed_set" "foo" {
|
||||
set_count = 2
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_computed_set.foo", "string_set.#", "2",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceComputedSet_ruleTest(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_computed_set" "foo" {
|
||||
rule {
|
||||
ip_protocol = "udp"
|
||||
cidr = "0.0.0.0/0"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceConfigMode(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
resource_as_attr = [
|
||||
{
|
||||
foo = "resource_as_attr 0"
|
||||
},
|
||||
{
|
||||
foo = "resource_as_attr 1"
|
||||
},
|
||||
]
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.1.foo", "resource_as_attr 1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
# Due to a preprocessing fixup we do in lang.EvalBlock, it's allowed
|
||||
# to specify resource_as_attr members using one or more nested blocks
|
||||
# instead of attribute syntax, if desired. This should be equivalent
|
||||
# to the previous config.
|
||||
#
|
||||
# This allowance is made for backward-compatibility with existing providers
|
||||
# before Terraform v0.12 that were expecting nested block types to also
|
||||
# support attribute syntax; it should not be used for any new use-cases.
|
||||
resource_as_attr {
|
||||
foo = "resource_as_attr 0"
|
||||
}
|
||||
resource_as_attr {
|
||||
foo = "resource_as_attr 1"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.1.foo", "resource_as_attr 1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
resource_as_attr = [
|
||||
{
|
||||
foo = "resource_as_attr 0 updated"
|
||||
},
|
||||
]
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0 updated"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
resource_as_attr = []
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "0"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckNoResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceConfigMode_nestedSet(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_config_mode" "foo" {
|
||||
resource_as_attr = []
|
||||
|
||||
nested_set {
|
||||
value = "a"
|
||||
}
|
||||
nested_set {
|
||||
value = "b"
|
||||
set = []
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// TestResourceDataDep_alignedCountScaleOut tests to make sure interpolation
|
||||
// works (namely without index errors) when a data source and a resource share
|
||||
// the same count variable during scale-out with an existing state.
|
||||
func TestResourceDataDep_alignedCountScaleOut(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testResourceDataDepConfig(2),
|
||||
},
|
||||
{
|
||||
Config: testResourceDataDepConfig(4),
|
||||
Check: resource.TestCheckOutput("out", "value_from_api,value_from_api,value_from_api,value_from_api"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestResourceDataDep_alignedCountScaleIn tests to make sure interpolation
|
||||
// works (namely without index errors) when a data source and a resource share
|
||||
// the same count variable during scale-in with an existing state.
|
||||
func TestResourceDataDep_alignedCountScaleIn(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testResourceDataDepConfig(4),
|
||||
},
|
||||
{
|
||||
Config: testResourceDataDepConfig(2),
|
||||
Check: resource.TestCheckOutput("out", "value_from_api,value_from_api"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestDataResourceDep_alignedCountScaleOut functions like
|
||||
// TestResourceDataDep_alignedCountScaleOut, but with the dependencies swapped
|
||||
// (resource now depends on data source, a pretty regular use case, but
|
||||
// included here to check for regressions).
|
||||
func TestDataResourceDep_alignedCountScaleOut(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testDataResourceDepConfig(2),
|
||||
},
|
||||
{
|
||||
Config: testDataResourceDepConfig(4),
|
||||
Check: resource.TestCheckOutput("out", "test,test,test,test"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestDataResourceDep_alignedCountScaleIn functions like
|
||||
// TestResourceDataDep_alignedCountScaleIn, but with the dependencies swapped
|
||||
// (resource now depends on data source, a pretty regular use case, but
|
||||
// included here to check for regressions).
|
||||
func TestDataResourceDep_alignedCountScaleIn(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testDataResourceDepConfig(4),
|
||||
},
|
||||
{
|
||||
Config: testDataResourceDepConfig(2),
|
||||
Check: resource.TestCheckOutput("out", "test,test"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestResourceResourceDep_alignedCountScaleOut functions like
|
||||
// TestResourceDataDep_alignedCountScaleOut, but with a resource-to-resource
|
||||
// dependency instead, a pretty regular use case, but included here to check
|
||||
// for regressions.
|
||||
func TestResourceResourceDep_alignedCountScaleOut(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testResourceResourceDepConfig(2),
|
||||
},
|
||||
{
|
||||
Config: testResourceResourceDepConfig(4),
|
||||
Check: resource.TestCheckOutput("out", "test,test,test,test"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestResourceResourceDep_alignedCountScaleIn functions like
|
||||
// TestResourceDataDep_alignedCountScaleIn, but with a resource-to-resource
|
||||
// dependency instead, a pretty regular use case, but included here to check
|
||||
// for regressions.
|
||||
func TestResourceResourceDep_alignedCountScaleIn(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testResourceResourceDepConfig(4),
|
||||
},
|
||||
{
|
||||
Config: testResourceResourceDepConfig(2),
|
||||
Check: resource.TestCheckOutput("out", "test,test"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testResourceDataDepConfig(count int) string {
|
||||
return fmt.Sprintf(`
|
||||
variable num {
|
||||
default = "%d"
|
||||
}
|
||||
|
||||
resource "test_resource" "foo" {
|
||||
count = "${var.num}"
|
||||
required = "yes"
|
||||
|
||||
required_map = {
|
||||
"foo" = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
data "test_data_source" "bar" {
|
||||
count = "${var.num}"
|
||||
input = "${test_resource.foo.*.computed_read_only[count.index]}"
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = "${join(",", data.test_data_source.bar.*.output)}"
|
||||
}
|
||||
`, count)
|
||||
}
|
||||
|
||||
func testDataResourceDepConfig(count int) string {
|
||||
return fmt.Sprintf(`
|
||||
variable num {
|
||||
default = "%d"
|
||||
}
|
||||
|
||||
data "test_data_source" "foo" {
|
||||
count = "${var.num}"
|
||||
input = "test"
|
||||
}
|
||||
|
||||
resource "test_resource" "bar" {
|
||||
count = "${var.num}"
|
||||
required = "yes"
|
||||
optional = "${data.test_data_source.foo.*.output[count.index]}"
|
||||
|
||||
required_map = {
|
||||
"foo" = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = "${join(",", test_resource.bar.*.optional)}"
|
||||
}
|
||||
`, count)
|
||||
}
|
||||
|
||||
func testResourceResourceDepConfig(count int) string {
|
||||
return fmt.Sprintf(`
|
||||
variable num {
|
||||
default = "%d"
|
||||
}
|
||||
|
||||
resource "test_resource" "foo" {
|
||||
count = "${var.num}"
|
||||
required = "yes"
|
||||
optional = "test"
|
||||
|
||||
required_map = {
|
||||
"foo" = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource" "bar" {
|
||||
count = "${var.num}"
|
||||
required = "yes"
|
||||
optional = "${test_resource.foo.*.optional[count.index]}"
|
||||
|
||||
required_map = {
|
||||
"foo" = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = "${join(",", test_resource.bar.*.optional)}"
|
||||
}
|
||||
`, count)
|
||||
}
|
|
@ -1,491 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var dataprocClusterSchema = map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"project": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"region": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "global",
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"labels": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
// GCP automatically adds two labels
|
||||
// 'goog-dataproc-cluster-uuid'
|
||||
// 'goog-dataproc-cluster-name'
|
||||
Computed: true,
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
if old != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
|
||||
"tag_set": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
|
||||
"cluster_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
|
||||
"delete_autogen_bucket": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
Removed: "If you need a bucket that can be deleted, please create" +
|
||||
"a new one and set the `staging_bucket` field",
|
||||
},
|
||||
|
||||
"staging_bucket": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"bucket": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"gce_cluster_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
|
||||
"zone": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"network": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ConflictsWith: []string{"cluster_config.0.gce_cluster_config.0.subnetwork"},
|
||||
},
|
||||
|
||||
"subnetwork": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ConflictsWith: []string{"cluster_config.0.gce_cluster_config.0.network"},
|
||||
},
|
||||
|
||||
"tags": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
"service_account": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"service_account_scopes": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
|
||||
"internal_ip_only": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"metadata": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"master_config": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"num_instances": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"image_uri": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"machine_type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"disk_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"num_local_ssds": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"boot_disk_size_gb": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"boot_disk_type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "pd-standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"accelerators": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"accelerator_type": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"accelerator_count": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"instance_names": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"preemptible_worker_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"num_instances": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"disk_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"num_local_ssds": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"boot_disk_size_gb": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"boot_disk_type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "pd-standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"instance_names": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"software_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"image_version": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"override_properties": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
"properties": {
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"initialization_action": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"script": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"timeout_sec": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 300,
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"encryption_config": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"kms_key_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDiffApply_dataprocCluster(t *testing.T) {
|
||||
priorAttrs := map[string]string{
|
||||
"cluster_config.#": "1",
|
||||
"cluster_config.0.bucket": "dataproc-1dc18cb2-116e-4e92-85ea-ff63a1bf2745-us-central1",
|
||||
"cluster_config.0.delete_autogen_bucket": "false",
|
||||
"cluster_config.0.encryption_config.#": "0",
|
||||
"cluster_config.0.gce_cluster_config.#": "1",
|
||||
"cluster_config.0.gce_cluster_config.0.internal_ip_only": "false",
|
||||
"cluster_config.0.gce_cluster_config.0.metadata.%": "0",
|
||||
"cluster_config.0.gce_cluster_config.0.network": "https://www.googleapis.com/compute/v1/projects/hc-terraform-testing/global/networks/default",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account": "",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.#": "7",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.1245378569": "https://www.googleapis.com/auth/bigtable.admin.table",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.1328717722": "https://www.googleapis.com/auth/devstorage.read_write",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.1693978638": "https://www.googleapis.com/auth/devstorage.full_control",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.172152165": "https://www.googleapis.com/auth/logging.write",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.2401844655": "https://www.googleapis.com/auth/bigquery",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.299921284": "https://www.googleapis.com/auth/bigtable.data",
|
||||
"cluster_config.0.gce_cluster_config.0.service_account_scopes.3804780973": "https://www.googleapis.com/auth/cloud.useraccounts.readonly",
|
||||
"cluster_config.0.gce_cluster_config.0.subnetwork": "",
|
||||
"cluster_config.0.gce_cluster_config.0.tags.#": "0",
|
||||
"cluster_config.0.gce_cluster_config.0.zone": "us-central1-f",
|
||||
"cluster_config.0.initialization_action.#": "0",
|
||||
"cluster_config.0.master_config.#": "1",
|
||||
"cluster_config.0.master_config.0.accelerators.#": "0",
|
||||
"cluster_config.0.master_config.0.disk_config.#": "1",
|
||||
"cluster_config.0.master_config.0.disk_config.0.boot_disk_size_gb": "500",
|
||||
"cluster_config.0.master_config.0.disk_config.0.boot_disk_type": "pd-standard",
|
||||
"cluster_config.0.master_config.0.disk_config.0.num_local_ssds": "0",
|
||||
"cluster_config.0.master_config.0.image_uri": "https://www.googleapis.com/compute/v1/projects/cloud-dataproc/global/images/dataproc-1-3-deb9-20190228-000000-rc01",
|
||||
"cluster_config.0.master_config.0.instance_names.#": "1",
|
||||
"cluster_config.0.master_config.0.instance_names.0": "dproc-cluster-test-2ww3c60iww-m",
|
||||
"cluster_config.0.master_config.0.machine_type": "n1-standard-4",
|
||||
"cluster_config.0.master_config.0.num_instances": "1",
|
||||
"cluster_config.0.preemptible_worker_config.#": "1",
|
||||
"cluster_config.0.preemptible_worker_config.0.disk_config.#": "1",
|
||||
"cluster_config.0.preemptible_worker_config.0.instance_names.#": "0",
|
||||
"cluster_config.0.preemptible_worker_config.0.num_instances": "0",
|
||||
"cluster_config.0.software_config.#": "1",
|
||||
"cluster_config.0.software_config.0.image_version": "1.3.28-deb9",
|
||||
"cluster_config.0.software_config.0.override_properties.%": "0",
|
||||
"cluster_config.0.software_config.0.properties.%": "14",
|
||||
"cluster_config.0.software_config.0.properties.capacity-scheduler:yarn.scheduler.capacity.root.default.ordering-policy": "fair",
|
||||
"cluster_config.0.software_config.0.properties.core:fs.gs.block.size": "134217728",
|
||||
"cluster_config.0.software_config.0.properties.core:fs.gs.metadata.cache.enable": "false",
|
||||
"cluster_config.0.software_config.0.properties.core:hadoop.ssl.enabled.protocols": "TLSv1,TLSv1.1,TLSv1.2",
|
||||
"cluster_config.0.software_config.0.properties.distcp:mapreduce.map.java.opts": "-Xmx768m",
|
||||
"cluster_config.0.software_config.0.properties.distcp:mapreduce.map.memory.mb": "1024",
|
||||
"cluster_config.0.software_config.0.properties.distcp:mapreduce.reduce.java.opts": "-Xmx768m",
|
||||
"cluster_config.0.software_config.0.properties.distcp:mapreduce.reduce.memory.mb": "1024",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.datanode.address": "0.0.0.0:9866",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.datanode.http.address": "0.0.0.0:9864",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.datanode.https.address": "0.0.0.0:9865",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.datanode.ipc.address": "0.0.0.0:9867",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.handler.count": "20",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.http-address": "0.0.0.0:9870",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.https-address": "0.0.0.0:9871",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.lifeline.rpc-address": "dproc-cluster-test-2ww3c60iww-m:8050",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.secondary.http-address": "0.0.0.0:9868",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.secondary.https-address": "0.0.0.0:9869",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.service.handler.count": "10",
|
||||
"cluster_config.0.software_config.0.properties.hdfs:dfs.namenode.servicerpc-address": "dproc-cluster-test-2ww3c60iww-m:8051",
|
||||
"cluster_config.0.software_config.0.properties.mapred-env:HADOOP_JOB_HISTORYSERVER_HEAPSIZE": "3840",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.job.maps": "21",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.job.reduce.slowstart.completedmaps": "0.95",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.job.reduces": "7",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.map.cpu.vcores": "1",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.map.java.opts": "-Xmx2457m",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.map.memory.mb": "3072",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.reduce.cpu.vcores": "1",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.reduce.java.opts": "-Xmx2457m",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.reduce.memory.mb": "3072",
|
||||
"cluster_config.0.software_config.0.properties.mapred:mapreduce.task.io.sort.mb": "256",
|
||||
"cluster_config.0.software_config.0.properties.mapred:yarn.app.mapreduce.am.command-opts": "-Xmx2457m",
|
||||
"cluster_config.0.software_config.0.properties.mapred:yarn.app.mapreduce.am.resource.cpu-vcores": "1",
|
||||
"cluster_config.0.software_config.0.properties.mapred:yarn.app.mapreduce.am.resource.mb": "3072",
|
||||
"cluster_config.0.software_config.0.properties.presto-jvm:MaxHeapSize": "12288m",
|
||||
"cluster_config.0.software_config.0.properties.presto:query.max-memory-per-node": "7372MB",
|
||||
"cluster_config.0.software_config.0.properties.presto:query.max-total-memory-per-node": "7372MB",
|
||||
"cluster_config.0.software_config.0.properties.spark-env:SPARK_DAEMON_MEMORY": "3840m",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.driver.maxResultSize": "1920m",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.driver.memory": "3840m",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.executor.cores": "2",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.executor.instances": "2",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.executor.memory": "5586m",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.executorEnv.OPENBLAS_NUM_THREADS": "1",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.scheduler.mode": "FAIR",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.sql.cbo.enabled": "true",
|
||||
"cluster_config.0.software_config.0.properties.spark:spark.yarn.am.memory": "640m",
|
||||
"cluster_config.0.software_config.0.properties.yarn-env:YARN_TIMELINESERVER_HEAPSIZE": "3840",
|
||||
"cluster_config.0.software_config.0.properties.yarn:yarn.nodemanager.resource.memory-mb": "12288",
|
||||
"cluster_config.0.software_config.0.properties.yarn:yarn.resourcemanager.nodemanager-graceful-decommission-timeout-secs": "86400",
|
||||
"cluster_config.0.software_config.0.properties.yarn:yarn.scheduler.maximum-allocation-mb": "12288",
|
||||
"cluster_config.0.software_config.0.properties.yarn:yarn.scheduler.minimum-allocation-mb": "1024",
|
||||
"cluster_config.0.staging_bucket": "",
|
||||
"id": "dproc-cluster-test-ktbyrniu4e",
|
||||
"labels.%": "4",
|
||||
"labels.goog-dataproc-cluster-name": "dproc-cluster-test-ktbyrniu4e",
|
||||
"labels.goog-dataproc-cluster-uuid": "d576c4e0-8fda-4ad1-abf5-ec951ab25855",
|
||||
"labels.goog-dataproc-location": "us-central1",
|
||||
"labels.key1": "value1",
|
||||
"tag_set.#": "0",
|
||||
}
|
||||
|
||||
diff := &terraform.InstanceDiff{
|
||||
Attributes: map[string]*terraform.ResourceAttrDiff{
|
||||
"labels.%": &terraform.ResourceAttrDiff{Old: "4", New: "1", NewComputed: false, NewRemoved: false, NewExtra: interface{}(nil), RequiresNew: false, Sensitive: false, Type: 0x0},
|
||||
"labels.goog-dataproc-cluster-name": &terraform.ResourceAttrDiff{Old: "dproc-cluster-test-ktbyrniu4e", New: "", NewComputed: false, NewRemoved: true, NewExtra: interface{}(nil), RequiresNew: false, Sensitive: false, Type: 0x0},
|
||||
"labels.goog-dataproc-cluster-uuid": &terraform.ResourceAttrDiff{Old: "d576c4e0-8fda-4ad1-abf5-ec951ab25855", New: "", NewComputed: false, NewRemoved: true, NewExtra: interface{}(nil), RequiresNew: false, Sensitive: false, Type: 0x0},
|
||||
"labels.goog-dataproc-location": &terraform.ResourceAttrDiff{Old: "us-central1", New: "", NewComputed: false, NewRemoved: true, NewExtra: interface{}(nil), RequiresNew: false, Sensitive: false, Type: 0x0},
|
||||
},
|
||||
}
|
||||
|
||||
newAttrs, err := diff.Apply(priorAttrs, (&schema.Resource{Schema: dataprocClusterSchema}).CoreConfigSchema())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the diff'ed labale elements should be removed
|
||||
delete(priorAttrs, "labels.goog-dataproc-cluster-name")
|
||||
delete(priorAttrs, "labels.goog-dataproc-cluster-uuid")
|
||||
delete(priorAttrs, "labels.goog-dataproc-location")
|
||||
priorAttrs["labels.%"] = "1"
|
||||
|
||||
// the missing required "name" should be added
|
||||
priorAttrs["name"] = ""
|
||||
|
||||
if !reflect.DeepEqual(priorAttrs, newAttrs) {
|
||||
t.Fatal(cmp.Diff(priorAttrs, newAttrs))
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceDefaults_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_defaults" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_string", "default string",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_bool", "1",
|
||||
),
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.#",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceDefaults_change(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_defaults" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_string", "default string",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_bool", "1",
|
||||
),
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.#",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_defaults" "foo" {
|
||||
default_string = "new"
|
||||
default_bool = false
|
||||
nested {
|
||||
optional = "nested"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_string", "new",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_bool", "false",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.2950978312.optional", "nested",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.2950978312.string", "default nested",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_defaults" "foo" {
|
||||
default_string = "new"
|
||||
default_bool = false
|
||||
nested {
|
||||
optional = "nested"
|
||||
string = "new"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_string", "new",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_bool", "false",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.782850362.optional", "nested",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.782850362.string", "new",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceDefaults_inSet(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_defaults" "foo" {
|
||||
nested {
|
||||
optional = "val"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_string", "default string",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "default_bool", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.2826070548.optional", "val",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_defaults.foo", "nested.2826070548.string", "default nested",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaults_emptyString(t *testing.T) {
|
||||
config := `
|
||||
resource "test_resource_defaults" "test" {
|
||||
default_string = ""
|
||||
}
|
||||
`
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: config,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_defaults.test", "default_string", ""),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
// an empty config should be ok, because no deprecated/removed fields are set.
|
||||
func TestResourceDeprecated_empty(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_deprecated" "foo" {
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Deprecated fields should still work
|
||||
func TestResourceDeprecated_deprecatedOK(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_deprecated" "foo" {
|
||||
map_deprecated = {
|
||||
"a" = "b",
|
||||
}
|
||||
set_block_deprecated {
|
||||
value = "1"
|
||||
}
|
||||
list_block_deprecated {
|
||||
value = "2"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Declaring an empty block should trigger the error
|
||||
func TestResourceDeprecated_removedBlocks(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_deprecated" "foo" {
|
||||
set_block_removed {
|
||||
}
|
||||
list_block_removed {
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("REMOVED"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceDiffSuppress_create(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "foo"
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestResourceDiffSuppress_update(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "foo"
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "bar"
|
||||
optional = "more"
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceDiffSuppress_updateIgnoreChanges(t *testing.T) {
|
||||
// None of these steps should replace the instance
|
||||
id := ""
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_diff_suppress.foo"]
|
||||
if id != "" && res.Primary.ID != id {
|
||||
return errors.New("expected no resource replacement")
|
||||
}
|
||||
id = res.Primary.ID
|
||||
return nil
|
||||
}
|
||||
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "foo"
|
||||
|
||||
network = "foo"
|
||||
subnetwork = "foo"
|
||||
|
||||
node_pool {
|
||||
name = "default-pool"
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = ["node_pool"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "foo"
|
||||
|
||||
network = "ignored"
|
||||
subnetwork = "ignored"
|
||||
|
||||
node_pool {
|
||||
name = "default-pool"
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = ["node_pool"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_diff_suppress" "foo" {
|
||||
val_to_upper = "foo"
|
||||
|
||||
network = "ignored"
|
||||
subnetwork = "ignored"
|
||||
|
||||
node_pool {
|
||||
name = "ignored"
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = ["node_pool"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceForceNew_create(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
triggers = {
|
||||
"a" = "foo"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestResourceForceNew_update(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
triggers = {
|
||||
"a" = "foo"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
triggers = {
|
||||
"a" = "bar"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
triggers = {
|
||||
"b" = "bar"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceForceNew_remove(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
triggers = {
|
||||
"a" = "bar"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_force_new" "foo" {
|
||||
} `),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// Tests GH-12183. This would previously cause a crash. More granular
|
||||
// unit tests are scattered through helper/schema and terraform core for
|
||||
// this.
|
||||
func TestResourceGH12183_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_gh12183" "a" {
|
||||
config {
|
||||
name = "hello"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource_gh12183" "b" {
|
||||
key = "${lookup(test_resource_gh12183.a.config[0], "name")}"
|
||||
config {
|
||||
name = "required"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: func(s *terraform.State) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceListSet_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list_set" "foo" {
|
||||
list {
|
||||
set {
|
||||
elem = "A"
|
||||
}
|
||||
set {
|
||||
elem = "B"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.1255198513.elem", "B"),
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.3554254475.elem", "A"),
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.#", "2"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list_set" "foo" {
|
||||
list {
|
||||
set {
|
||||
elem = "B"
|
||||
}
|
||||
set {
|
||||
elem = "C"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.1255198513.elem", "B"),
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.1037565863.elem", "C"),
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "list.0.set.#", "2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceListSet_updateNested(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list_set" "foo" {
|
||||
replication_configuration {
|
||||
role = "role_id"
|
||||
rules {
|
||||
id = "foobar"
|
||||
status = "Enabled"
|
||||
priority = 42
|
||||
filter {
|
||||
tags = {
|
||||
ReplicateMe = "Yes"
|
||||
}
|
||||
}
|
||||
destination {
|
||||
bucket = "bucket_id"
|
||||
storage_class = "STANDARD"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "replication_configuration.0.rules.#", "1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list_set" "foo" {
|
||||
replication_configuration {
|
||||
role = "role_id"
|
||||
rules {
|
||||
id = "foobar"
|
||||
status = "Enabled"
|
||||
priority = 42
|
||||
filter {
|
||||
prefix = "foo"
|
||||
tags = {
|
||||
ReplicateMe = "Yes"
|
||||
AnotherTag = "OK"
|
||||
}
|
||||
}
|
||||
destination {
|
||||
bucket = "bucket_id"
|
||||
storage_class = "STANDARD"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list_set.foo", "replication_configuration.0.rules.#", "1"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,566 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
// an empty config should be ok, because no deprecated/removed fields are set.
|
||||
func TestResourceList_changed(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "a"
|
||||
int = 1
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.string", "a",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.int", "1",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "a"
|
||||
int = 1
|
||||
}
|
||||
|
||||
list_block {
|
||||
string = "b"
|
||||
int = 2
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.#", "2",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.string", "a",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.int", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.1.string", "b",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.1.int", "2",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "a"
|
||||
int = 1
|
||||
}
|
||||
|
||||
list_block {
|
||||
string = "c"
|
||||
int = 2
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.#", "2",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.string", "a",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.int", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.1.string", "c",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.1.int", "2",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_mapList(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
variable "map" {
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
resource "test_resource_list" "foo" {
|
||||
map_list = [
|
||||
{
|
||||
a = "1"
|
||||
},
|
||||
var.map
|
||||
]
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "map_list.1", "",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_sublist(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
sublist_block {
|
||||
string = "a"
|
||||
int = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.sublist_block.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.sublist_block.0.string", "a",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.sublist_block.0.int", "1",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_interpolationChanges(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "x"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
string = test_resource_list.foo.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.string", "x",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.string", "testId",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "baz" {
|
||||
list_block {
|
||||
string = "x"
|
||||
int = 1
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
string = test_resource_list.baz.id
|
||||
int = 3
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.baz", "list_block.0.string", "x",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.string", "testId",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_removedForcesNew(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
force_new = "ok"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.foo", "list_block.0.force_new", "ok",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_emptyStrings(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
sublist = ["a", ""]
|
||||
}
|
||||
|
||||
list_block {
|
||||
sublist = [""]
|
||||
}
|
||||
|
||||
list_block {
|
||||
sublist = ["", "c", ""]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.0.sublist.0", "a"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.0.sublist.1", ""),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.1.sublist.0", ""),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.2.sublist.0", ""),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.2.sublist.1", "c"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.2.sublist.2", ""),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
sublist = [""]
|
||||
}
|
||||
|
||||
list_block {
|
||||
sublist = []
|
||||
}
|
||||
|
||||
list_block {
|
||||
sublist = ["", "c"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.0.sublist.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.0.sublist.0", ""),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.1.sublist.#", "0"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.2.sublist.1", "c"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "list_block.2.sublist.#", "2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_addRemove(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "computed_list.#", "0"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "dependent_list.#", "0"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
dependent_list {
|
||||
val = "a"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "computed_list.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "dependent_list.#", "1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "computed_list.#", "0"),
|
||||
resource.TestCheckResourceAttr("test_resource_list.foo", "dependent_list.#", "0"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_planUnknownInterpolation(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "x"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
sublist = [
|
||||
test_resource_list.foo.list_block[0].string,
|
||||
]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.sublist.0", "x",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "x"
|
||||
}
|
||||
dependent_list {
|
||||
val = "y"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
sublist = [
|
||||
test_resource_list.foo.computed_list[0],
|
||||
]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.sublist.0", "y",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
list_block {
|
||||
string = "x"
|
||||
}
|
||||
dependent_list {
|
||||
val = "z"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
sublist = [
|
||||
test_resource_list.foo.computed_list[0],
|
||||
]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.sublist.0", "z",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_planUnknownInterpolationList(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
dependent_list {
|
||||
val = "y"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
sublist_block_optional {
|
||||
list = test_resource_list.foo.computed_list
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.sublist_block_optional.0.list.0", "y",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "foo" {
|
||||
dependent_list {
|
||||
val = "z"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "bar" {
|
||||
list_block {
|
||||
sublist_block_optional {
|
||||
list = test_resource_list.foo.computed_list
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_list.bar", "list_block.0.sublist_block_optional.0.list.0", "z",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_dynamicList(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "a" {
|
||||
dependent_list {
|
||||
val = "a"
|
||||
}
|
||||
|
||||
dependent_list {
|
||||
val = "b"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "b" {
|
||||
list_block {
|
||||
string = "constant"
|
||||
}
|
||||
dynamic "list_block" {
|
||||
for_each = test_resource_list.a.computed_list
|
||||
content {
|
||||
string = list_block.value
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceList_dynamicMinItems(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
variable "a" {
|
||||
type = list(number)
|
||||
default = [1]
|
||||
}
|
||||
|
||||
resource "test_resource_list" "b" {
|
||||
dynamic "min_items" {
|
||||
for_each = var.a
|
||||
content {
|
||||
val = "foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile(`attribute supports 2`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "a" {
|
||||
dependent_list {
|
||||
val = "a"
|
||||
}
|
||||
|
||||
dependent_list {
|
||||
val = "b"
|
||||
}
|
||||
}
|
||||
resource "test_resource_list" "b" {
|
||||
list_block {
|
||||
string = "constant"
|
||||
}
|
||||
dynamic "min_items" {
|
||||
for_each = test_resource_list.a.computed_list
|
||||
content {
|
||||
val = min_items.value
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceMap_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: `
|
||||
resource "test_resource_map" "foobar" {
|
||||
name = "test"
|
||||
map_of_three = {
|
||||
one = "one"
|
||||
two = "two"
|
||||
empty = ""
|
||||
}
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "map_of_three.empty", "",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceMap_basicWithVars(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: `
|
||||
variable "a" {
|
||||
default = "a"
|
||||
}
|
||||
|
||||
variable "b" {
|
||||
default = "b"
|
||||
}
|
||||
|
||||
resource "test_resource_map" "foobar" {
|
||||
name = "test"
|
||||
map_of_three = {
|
||||
one = var.a
|
||||
two = var.b
|
||||
empty = ""
|
||||
}
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceMap_computedMap(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: `
|
||||
resource "test_resource_map" "foobar" {
|
||||
name = "test"
|
||||
map_of_three = {
|
||||
one = "one"
|
||||
two = "two"
|
||||
empty = ""
|
||||
}
|
||||
map_values = {
|
||||
a = "1"
|
||||
b = "2"
|
||||
}
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.a", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.b", "2",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: `
|
||||
resource "test_resource_map" "foobar" {
|
||||
name = "test"
|
||||
map_of_three = {
|
||||
one = "one"
|
||||
two = "two"
|
||||
empty = ""
|
||||
}
|
||||
map_values = {
|
||||
a = "3"
|
||||
b = "4"
|
||||
}
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.a", "3",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.b", "4",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: `
|
||||
resource "test_resource_map" "foobar" {
|
||||
name = "test"
|
||||
map_of_three = {
|
||||
one = "one"
|
||||
two = "two"
|
||||
empty = ""
|
||||
}
|
||||
map_values = {
|
||||
a = "3"
|
||||
}
|
||||
}`,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.a", "3",
|
||||
),
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_map.foobar", "computed_map.b",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceNestedId_unknownId(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_id" "foo" {
|
||||
}
|
||||
resource "test_resource_nested_id" "bar" {
|
||||
list_block {
|
||||
id = test_resource_nested_id.foo.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_nested_id.bar", "list_block.0.id", "testId"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,653 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceNestedSet_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_basicImport(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
ImportState: true,
|
||||
ResourceName: "test_resource_nested_set.foo",
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
ImportStateCheck: func(ss []*terraform.InstanceState) error {
|
||||
for _, s := range ss {
|
||||
if s.Attributes["multi.#"] != "0" ||
|
||||
s.Attributes["single.#"] != "0" ||
|
||||
s.Attributes["type_list.#"] != "0" ||
|
||||
s.Attributes["with_list.#"] != "0" {
|
||||
return fmt.Errorf("missing blocks in imported state:\n%s", s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// The set should not be generated because of it's computed value
|
||||
func TestResourceNestedSet_noSet(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested_set.foo"]
|
||||
for k, v := range res.Primary.Attributes {
|
||||
if strings.HasPrefix(k, "single") && k != "single.#" {
|
||||
return fmt.Errorf("unexpected set value: %s:%s", k, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// the empty type_list must be passed to the provider with 1 nil element
|
||||
func TestResourceNestedSet_emptyBlock(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
type_list {
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_nested_set.foo", "type_list.#", "1"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_emptyNestedListBlock(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested_set.foo"]
|
||||
found := false
|
||||
for k := range res.Primary.Attributes {
|
||||
if !regexp.MustCompile(`^with_list\.\d+\.list_block\.`).MatchString(k) {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("with_list.X.list_block not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
with_list {
|
||||
required = "ok"
|
||||
list_block {
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestResourceNestedSet_emptyNestedList(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested_set.foo"]
|
||||
found := false
|
||||
for k, v := range res.Primary.Attributes {
|
||||
if regexp.MustCompile(`^with_list\.\d+\.list\.#$`).MatchString(k) {
|
||||
found = true
|
||||
if v != "0" {
|
||||
return fmt.Errorf("expected empty list: %s, got %s", k, v)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("with_list.X.nested_list not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
with_list {
|
||||
required = "ok"
|
||||
list = []
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_addRemove(t *testing.T) {
|
||||
var id string
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested_set.foo"]
|
||||
if res.Primary.ID == id {
|
||||
return errors.New("expected new resource")
|
||||
}
|
||||
id = res.Primary.ID
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
checkFunc,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.foo", "single.#", "1",
|
||||
),
|
||||
// the hash of single seems to change here, so we're not
|
||||
// going to test for "value" directly
|
||||
// FIXME: figure out why the set hash changes
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.foo", "single.#", "0",
|
||||
),
|
||||
checkFunc,
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
optional = "baz"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestResourceNestedSet_multiAddRemove(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
optional = "bar"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = "val"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = "new"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = "new"
|
||||
optional_int = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "bar"
|
||||
optional = "baz"
|
||||
}
|
||||
multi {
|
||||
set {
|
||||
required = "new"
|
||||
optional_int = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
optional = true
|
||||
single {
|
||||
value = "bar"
|
||||
optional = "baz"
|
||||
}
|
||||
multi {
|
||||
set {
|
||||
required = "new"
|
||||
optional_int = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_forceNewEmptyString(t *testing.T) {
|
||||
var id string
|
||||
step := 0
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested_set.foo"]
|
||||
defer func() {
|
||||
step++
|
||||
id = res.Primary.ID
|
||||
}()
|
||||
|
||||
if step == 2 && res.Primary.ID == id {
|
||||
// setting an empty string currently does not trigger ForceNew, but
|
||||
// it should in the future.
|
||||
return nil
|
||||
}
|
||||
|
||||
if res.Primary.ID == id {
|
||||
return errors.New("expected new resource")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = "val"
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
force_new = ""
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_setWithList(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
with_list {
|
||||
required = "bar"
|
||||
list = ["initial value"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
with_list {
|
||||
required = "bar"
|
||||
list = ["second value"]
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// This is the same as forceNewEmptyString, but we start with the empty value,
|
||||
// instead of changing it.
|
||||
func TestResourceNestedSet_nestedSetEmptyString(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
set {
|
||||
required = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.foo", "multi.529860700.set.4196279896.required", "",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_emptySet(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
multi {
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.foo", "multi.#", "1",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_multipleUnknownSetElements(t *testing.T) {
|
||||
checkFunc := func(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "a" {
|
||||
}
|
||||
|
||||
resource "test_resource_nested_set" "b" {
|
||||
}
|
||||
|
||||
resource "test_resource_nested_set" "c" {
|
||||
multi {
|
||||
optional = test_resource_nested_set.a.id
|
||||
}
|
||||
multi {
|
||||
optional = test_resource_nested_set.b.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: checkFunc,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_interpolationChanges(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
single {
|
||||
value = "x"
|
||||
}
|
||||
}
|
||||
resource "test_resource_nested_set" "bar" {
|
||||
single {
|
||||
value = test_resource_nested_set.foo.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.foo", "single.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.bar", "single.#", "1",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested_set" "baz" {
|
||||
single {
|
||||
value = "x"
|
||||
}
|
||||
}
|
||||
resource "test_resource_nested_set" "bar" {
|
||||
single {
|
||||
value = test_resource_nested_set.baz.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.baz", "single.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested_set.bar", "single.#", "1",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNestedSet_dynamicSetBlock(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource" "a" {
|
||||
required = "ok"
|
||||
required_map = {
|
||||
a = "b"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource_nested_set" "foo" {
|
||||
dynamic "with_list" {
|
||||
iterator = thing
|
||||
for_each = test_resource.a.computed_list
|
||||
content {
|
||||
required = thing.value
|
||||
list = [thing.key]
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceNested_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
nested {
|
||||
string = "val"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.#", "1",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.1877647874.string", "val",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "list_block.0.sub_list_block.0.bool", "false",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNested_addRemove(t *testing.T) {
|
||||
var id string
|
||||
idCheck := func(s *terraform.State) error {
|
||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||
res := root.Resources["test_resource_nested.foo"]
|
||||
if res.Primary.ID == id {
|
||||
return errors.New("expected new resource")
|
||||
}
|
||||
id = res.Primary.ID
|
||||
return nil
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.#", "0",
|
||||
),
|
||||
// Checking for a count of 0 and a nonexistent count should
|
||||
// now be the same operation.
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_nested.foo", "nested.#",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
nested {
|
||||
string = "val"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.1877647874.string", "val",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
optional = true
|
||||
nested {
|
||||
string = "val"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.1877647874.string", "val",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "optional", "true",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
nested {
|
||||
string = "val"
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.1877647874.string", "val",
|
||||
),
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_nested.foo", "optional",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
nested {
|
||||
string = "val"
|
||||
optional = true
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.2994502535.string", "val",
|
||||
),
|
||||
resource.TestCheckResourceAttr(
|
||||
"test_resource_nested.foo", "nested.2994502535.optional", "true",
|
||||
),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
idCheck,
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"test_resource_nested.foo", "nested.#",
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceNested_dynamic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_nested" "foo" {
|
||||
dynamic "nested" {
|
||||
for_each = [["a"], []]
|
||||
content {
|
||||
string = join(",", nested.value)
|
||||
optional = false
|
||||
dynamic "nested_again" {
|
||||
for_each = nested.value
|
||||
content {
|
||||
string = nested_again.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
Check: func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources["test_resource_nested.foo"]
|
||||
if !ok {
|
||||
return errors.New("missing resource in state")
|
||||
}
|
||||
|
||||
got := rs.Primary.Attributes
|
||||
want := map[string]string{
|
||||
"nested.#": "2",
|
||||
"nested.33842314.string": "a",
|
||||
"nested.33842314.optional": "false",
|
||||
"nested.33842314.nested_again.#": "1",
|
||||
"nested.33842314.nested_again.936590934.string": "a",
|
||||
"nested.140280279.string": "",
|
||||
"nested.140280279.optional": "false",
|
||||
"nested.140280279.nested_again.#": "0",
|
||||
"list_block.#": "1",
|
||||
"list_block.0.sub_list_block.#": "1",
|
||||
"list_block.0.sub_list_block.0.bool": "false",
|
||||
"list_block.0.sub_list_block.0.set.#": "0",
|
||||
}
|
||||
delete(got, "id") // it's random, so not useful for testing
|
||||
|
||||
if !cmp.Equal(got, want) {
|
||||
return errors.New("wrong result\n" + cmp.Diff(want, got))
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceProviderMeta_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
terraform {
|
||||
provider_meta "test" {
|
||||
foo = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource_provider_meta" "foo" {
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResource_dynamicRequiredMinItems(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: `
|
||||
resource "test_resource_required_min" "a" {
|
||||
}
|
||||
`,
|
||||
ExpectError: regexp.MustCompile(`"required_min_items" blocks are required`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "a" {
|
||||
dependent_list {
|
||||
val = "a"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource_required_min" "b" {
|
||||
dynamic "required_min_items" {
|
||||
for_each = test_resource_list.a.computed_list
|
||||
content {
|
||||
val = required_min_items.value
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile(`required_min_items: attribute supports 2 item as a minimum`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_list" "c" {
|
||||
dependent_list {
|
||||
val = "a"
|
||||
}
|
||||
|
||||
dependent_list {
|
||||
val = "b"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource_required_min" "b" {
|
||||
dynamic "required_min_items" {
|
||||
for_each = test_resource_list.c.computed_list
|
||||
content {
|
||||
val = required_min_items.value
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceStateFunc_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.TestCheckNoResourceAttr("test_resource_state_func.foo", "state_func"),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
state_func = "data"
|
||||
state_func_value = "data"
|
||||
}
|
||||
`),
|
||||
Check: resource.TestCheckResourceAttr("test_resource_state_func.foo", "state_func", stateFuncHash("data")),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
}
|
||||
`),
|
||||
Check: resource.TestCheckNoResourceAttr("test_resource_state_func.foo", "state_func"),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
optional = "added"
|
||||
state_func = "data"
|
||||
state_func_value = "data"
|
||||
}
|
||||
`),
|
||||
Check: resource.TestCheckResourceAttr("test_resource_state_func.foo", "state_func", stateFuncHash("data")),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
optional = "added"
|
||||
state_func = "changed"
|
||||
state_func_value = "changed"
|
||||
}
|
||||
`),
|
||||
Check: resource.TestCheckResourceAttr("test_resource_state_func.foo", "state_func", stateFuncHash("changed")),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceStateFunc_getOkSetElem(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_state_func" "foo" {
|
||||
}
|
||||
|
||||
resource "test_resource_state_func" "bar" {
|
||||
set_block {
|
||||
required = "foo"
|
||||
optional = test_resource_state_func.foo.id
|
||||
}
|
||||
set_block {
|
||||
required = test_resource_state_func.foo.id
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,158 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestResourceTimeout_create(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
create_delay = "2s"
|
||||
timeouts {
|
||||
create = "1s"
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("timeout while creating resource"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// start with the default, then modify it
|
||||
func TestResourceTimeout_defaults(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
update_delay = "1ms"
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
update_delay = "2ms"
|
||||
timeouts {
|
||||
update = "3s"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
update_delay = "2s"
|
||||
delete_delay = "2s"
|
||||
timeouts {
|
||||
delete = "3s"
|
||||
update = "3s"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
// delete "foo"
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "bar" {
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceTimeout_delete(t *testing.T) {
|
||||
// If the delete timeout isn't saved until destroy, the cleanup here will
|
||||
// fail because the default is only 20m.
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
delete_delay = "25m"
|
||||
timeouts {
|
||||
delete = "30m"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestResourceTimeout_update(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
update_delay = "1s"
|
||||
timeouts {
|
||||
update = "1s"
|
||||
}
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
update_delay = "2s"
|
||||
timeouts {
|
||||
update = "1s"
|
||||
}
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("timeout while updating resource"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceTimeout_read(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckResourceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
}
|
||||
`),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
read_delay = "30m"
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("timeout while reading resource"),
|
||||
},
|
||||
// we need to remove the read_delay so that the resource can be
|
||||
// destroyed in the final step, but expect an error here from the
|
||||
// pre-existing delay.
|
||||
resource.TestStep{
|
||||
Config: strings.TrimSpace(`
|
||||
resource "test_resource_timeout" "foo" {
|
||||
}
|
||||
`),
|
||||
ExpectError: regexp.MustCompile("timeout while reading resource"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
// TestResourceWithCustomDiff test custom diff behaviour.
|
||||
func TestResourceWithCustomDiff(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: resourceWithCustomDiffConfig(false),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "computed", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "index", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "list.#", "1"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "list.0", "dc1"),
|
||||
),
|
||||
ExpectNonEmptyPlan: true,
|
||||
},
|
||||
{
|
||||
Config: resourceWithCustomDiffConfig(false),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "computed", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "index", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "list.#", "2"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "list.0", "dc2"),
|
||||
resource.TestCheckResourceAttr("test_resource_with_custom_diff.foo", "list.1", "dc3"),
|
||||
resource.TestCheckNoResourceAttr("test_resource_with_custom_diff.foo", "list.2"),
|
||||
),
|
||||
ExpectNonEmptyPlan: true,
|
||||
},
|
||||
{
|
||||
Config: resourceWithCustomDiffConfig(true),
|
||||
ExpectError: regexp.MustCompile("veto is true, diff vetoed"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func resourceWithCustomDiffConfig(veto bool) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "test_resource_with_custom_diff" "foo" {
|
||||
required = "yep"
|
||||
veto = %t
|
||||
}
|
||||
`, veto)
|
||||
}
|
|
@ -7,8 +7,8 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvisioner_impl(t *testing.T) {
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"runtime"
|
||||
|
||||
"github.com/armon/circbuf"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/mitchellh/go-linereader"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvisioner_impl(t *testing.T) {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/communicator"
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/mitchellh/go-linereader"
|
||||
)
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/communicator"
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvisioner_impl(t *testing.T) {
|
||||
|
|
|
@ -33,7 +33,7 @@ func TestProviderDevOverrides(t *testing.T) {
|
|||
// such as if it stops being buildable into an independent executable.
|
||||
providerExeDir := filepath.Join(tf.WorkDir(), "pkgdir")
|
||||
providerExePrefix := filepath.Join(providerExeDir, "terraform-provider-test_")
|
||||
providerExe := e2e.GoBuild("github.com/hashicorp/terraform/builtin/bins/provider-test", providerExePrefix)
|
||||
providerExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/legacy/builtin/bins/provider-test", providerExePrefix)
|
||||
t.Logf("temporary provider executable is %s", providerExe)
|
||||
|
||||
err := ioutil.WriteFile(filepath.Join(tf.WorkDir(), "dev.tfrc"), []byte(fmt.Sprintf(`
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/terraform/builtin/providers/test"
|
||||
"github.com/hashicorp/terraform/e2e"
|
||||
grpcplugin "github.com/hashicorp/terraform/helper/plugin"
|
||||
"github.com/hashicorp/terraform/internal/legacy/builtin/providers/test"
|
||||
grpcplugin "github.com/hashicorp/terraform/internal/legacy/helper/plugin"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/communicator/ssh"
|
||||
"github.com/hashicorp/terraform/communicator/winrm"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
// Communicator is an interface that must be implemented by all communicators
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
// MockCommunicator is an implementation of Communicator that can be used for tests.
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestCommunicator_new(t *testing.T) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator/shared"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
sshagent "github.com/xanzy/ssh-agent"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
|
|
@ -3,7 +3,7 @@ package ssh
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestProvisioner_connInfo(t *testing.T) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/masterzen/winrm"
|
||||
"github.com/packer-community/winrmcp/winrmcp"
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/dylanmei/winrmtest"
|
||||
"github.com/hashicorp/terraform/communicator/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func newMockWinRMServer(t *testing.T) *winrmtest.Remote {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator/shared"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package winrm
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func TestProvisioner_defaultHTTPSPort(t *testing.T) {
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NotFoundError struct {
|
||||
LastError error
|
||||
LastRequest interface{}
|
||||
LastResponse interface{}
|
||||
Message string
|
||||
Retries int
|
||||
}
|
||||
|
||||
func (e *NotFoundError) Error() string {
|
||||
if e.Message != "" {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
if e.Retries > 0 {
|
||||
return fmt.Sprintf("couldn't find resource (%d retries)", e.Retries)
|
||||
}
|
||||
|
||||
return "couldn't find resource"
|
||||
}
|
||||
|
||||
// UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending
|
||||
type UnexpectedStateError struct {
|
||||
LastError error
|
||||
State string
|
||||
ExpectedState []string
|
||||
}
|
||||
|
||||
func (e *UnexpectedStateError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"unexpected state '%s', wanted target '%s'. last error: %s",
|
||||
e.State,
|
||||
strings.Join(e.ExpectedState, ", "),
|
||||
e.LastError,
|
||||
)
|
||||
}
|
||||
|
||||
// TimeoutError is returned when WaitForState times out
|
||||
type TimeoutError struct {
|
||||
LastError error
|
||||
LastState string
|
||||
Timeout time.Duration
|
||||
ExpectedState []string
|
||||
}
|
||||
|
||||
func (e *TimeoutError) Error() string {
|
||||
expectedState := "resource to be gone"
|
||||
if len(e.ExpectedState) > 0 {
|
||||
expectedState = fmt.Sprintf("state to become '%s'", strings.Join(e.ExpectedState, ", "))
|
||||
}
|
||||
|
||||
extraInfo := make([]string, 0)
|
||||
if e.LastState != "" {
|
||||
extraInfo = append(extraInfo, fmt.Sprintf("last state: '%s'", e.LastState))
|
||||
}
|
||||
if e.Timeout > 0 {
|
||||
extraInfo = append(extraInfo, fmt.Sprintf("timeout: %s", e.Timeout.String()))
|
||||
}
|
||||
|
||||
suffix := ""
|
||||
if len(extraInfo) > 0 {
|
||||
suffix = fmt.Sprintf(" (%s)", strings.Join(extraInfo, ", "))
|
||||
}
|
||||
|
||||
if e.LastError != nil {
|
||||
return fmt.Sprintf("timeout while waiting for %s%s: %s",
|
||||
expectedState, suffix, e.LastError)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("timeout while waiting for %s%s",
|
||||
expectedState, suffix)
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/plugin"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
)
|
||||
|
||||
// GRPCTestProvider takes a legacy ResourceProvider, wraps it in the new GRPC
|
||||
// shim and starts it in a grpc server using an inmem connection. It returns a
|
||||
// GRPCClient for this new server to test the shimmed resource provider.
|
||||
func GRPCTestProvider(rp terraform.ResourceProvider) providers.Interface {
|
||||
listener := bufconn.Listen(256 * 1024)
|
||||
grpcServer := grpc.NewServer()
|
||||
|
||||
p := plugin.NewGRPCProviderServerShim(rp)
|
||||
proto.RegisterProviderServer(grpcServer, p)
|
||||
|
||||
go grpcServer.Serve(listener)
|
||||
|
||||
conn, err := grpc.Dial("", grpc.WithDialer(func(string, time.Duration) (net.Conn, error) {
|
||||
return listener.Dial()
|
||||
}), grpc.WithInsecure())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var pp tfplugin.GRPCProviderPlugin
|
||||
client, _ := pp.GRPCClient(context.Background(), nil, conn)
|
||||
|
||||
grpcClient := client.(*tfplugin.GRPCProvider)
|
||||
grpcClient.TestServer = grpcServer
|
||||
|
||||
return grpcClient
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const UniqueIdPrefix = `terraform-`
|
||||
|
||||
// idCounter is a monotonic counter for generating ordered unique ids.
|
||||
var idMutex sync.Mutex
|
||||
var idCounter uint32
|
||||
|
||||
// Helper for a resource to generate a unique identifier w/ default prefix
|
||||
func UniqueId() string {
|
||||
return PrefixedUniqueId(UniqueIdPrefix)
|
||||
}
|
||||
|
||||
// UniqueIDSuffixLength is the string length of the suffix generated by
|
||||
// PrefixedUniqueId. This can be used by length validation functions to
|
||||
// ensure prefixes are the correct length for the target field.
|
||||
const UniqueIDSuffixLength = 26
|
||||
|
||||
// Helper for a resource to generate a unique identifier w/ given prefix
|
||||
//
|
||||
// After the prefix, the ID consists of an incrementing 26 digit value (to match
|
||||
// previous timestamp output). After the prefix, the ID consists of a timestamp
|
||||
// and an incrementing 8 hex digit value The timestamp means that multiple IDs
|
||||
// created with the same prefix will sort in the order of their creation, even
|
||||
// across multiple terraform executions, as long as the clock is not turned back
|
||||
// between calls, and as long as any given terraform execution generates fewer
|
||||
// than 4 billion IDs.
|
||||
func PrefixedUniqueId(prefix string) string {
|
||||
// Be precise to 4 digits of fractional seconds, but remove the dot before the
|
||||
// fractional seconds.
|
||||
timestamp := strings.Replace(
|
||||
time.Now().UTC().Format("20060102150405.0000"), ".", "", 1)
|
||||
|
||||
idMutex.Lock()
|
||||
defer idMutex.Unlock()
|
||||
idCounter++
|
||||
return fmt.Sprintf("%s%s%08x", prefix, timestamp, idCounter)
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var allDigits = regexp.MustCompile(`^\d+$`)
|
||||
var allHex = regexp.MustCompile(`^[a-f0-9]+$`)
|
||||
|
||||
func TestUniqueId(t *testing.T) {
|
||||
split := func(rest string) (timestamp, increment string) {
|
||||
return rest[:18], rest[18:]
|
||||
}
|
||||
|
||||
iterations := 10000
|
||||
ids := make(map[string]struct{})
|
||||
var id, lastId string
|
||||
for i := 0; i < iterations; i++ {
|
||||
id = UniqueId()
|
||||
|
||||
if _, ok := ids[id]; ok {
|
||||
t.Fatalf("Got duplicated id! %s", id)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(id, UniqueIdPrefix) {
|
||||
t.Fatalf("Unique ID didn't have terraform- prefix! %s", id)
|
||||
}
|
||||
|
||||
rest := strings.TrimPrefix(id, UniqueIdPrefix)
|
||||
|
||||
if len(rest) != UniqueIDSuffixLength {
|
||||
t.Fatalf("PrefixedUniqueId is out of sync with UniqueIDSuffixLength, post-prefix part has wrong length! %s", rest)
|
||||
}
|
||||
|
||||
timestamp, increment := split(rest)
|
||||
|
||||
if !allDigits.MatchString(timestamp) {
|
||||
t.Fatalf("Timestamp not all digits! %s", timestamp)
|
||||
}
|
||||
|
||||
if !allHex.MatchString(increment) {
|
||||
t.Fatalf("Increment part not all hex! %s", increment)
|
||||
}
|
||||
|
||||
if lastId != "" && lastId >= id {
|
||||
t.Fatalf("IDs not ordered! %s vs %s", lastId, id)
|
||||
}
|
||||
|
||||
ids[id] = struct{}{}
|
||||
lastId = id
|
||||
}
|
||||
|
||||
id1 := UniqueId()
|
||||
time.Sleep(time.Millisecond)
|
||||
id2 := UniqueId()
|
||||
timestamp1, _ := split(strings.TrimPrefix(id1, UniqueIdPrefix))
|
||||
timestamp2, _ := split(strings.TrimPrefix(id2, UniqueIdPrefix))
|
||||
|
||||
if timestamp1 == timestamp2 {
|
||||
t.Fatalf("Timestamp part should update at least once a millisecond %s %s",
|
||||
id1, id2)
|
||||
}
|
||||
}
|
|
@ -1,259 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
var refreshGracePeriod = 30 * time.Second
|
||||
|
||||
// StateRefreshFunc is a function type used for StateChangeConf that is
|
||||
// responsible for refreshing the item being watched for a state change.
|
||||
//
|
||||
// It returns three results. `result` is any object that will be returned
|
||||
// as the final object after waiting for state change. This allows you to
|
||||
// return the final updated object, for example an EC2 instance after refreshing
|
||||
// it.
|
||||
//
|
||||
// `state` is the latest state of that object. And `err` is any error that
|
||||
// may have happened while refreshing the state.
|
||||
type StateRefreshFunc func() (result interface{}, state string, err error)
|
||||
|
||||
// StateChangeConf is the configuration struct used for `WaitForState`.
|
||||
type StateChangeConf struct {
|
||||
Delay time.Duration // Wait this time before starting checks
|
||||
Pending []string // States that are "allowed" and will continue trying
|
||||
Refresh StateRefreshFunc // Refreshes the current state
|
||||
Target []string // Target state
|
||||
Timeout time.Duration // The amount of time to wait before timeout
|
||||
MinTimeout time.Duration // Smallest time to wait before refreshes
|
||||
PollInterval time.Duration // Override MinTimeout/backoff and only poll this often
|
||||
NotFoundChecks int // Number of times to allow not found
|
||||
|
||||
// This is to work around inconsistent APIs
|
||||
ContinuousTargetOccurence int // Number of times the Target state has to occur continuously
|
||||
}
|
||||
|
||||
// WaitForState watches an object and waits for it to achieve the state
|
||||
// specified in the configuration using the specified Refresh() func,
|
||||
// waiting the number of seconds specified in the timeout configuration.
|
||||
//
|
||||
// If the Refresh function returns an error, exit immediately with that error.
|
||||
//
|
||||
// If the Refresh function returns a state other than the Target state or one
|
||||
// listed in Pending, return immediately with an error.
|
||||
//
|
||||
// If the Timeout is exceeded before reaching the Target state, return an
|
||||
// error.
|
||||
//
|
||||
// Otherwise, the result is the result of the first call to the Refresh function to
|
||||
// reach the target state.
|
||||
func (conf *StateChangeConf) WaitForState() (interface{}, error) {
|
||||
log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)
|
||||
|
||||
notfoundTick := 0
|
||||
targetOccurence := 0
|
||||
|
||||
// Set a default for times to check for not found
|
||||
if conf.NotFoundChecks == 0 {
|
||||
conf.NotFoundChecks = 20
|
||||
}
|
||||
|
||||
if conf.ContinuousTargetOccurence == 0 {
|
||||
conf.ContinuousTargetOccurence = 1
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Result interface{}
|
||||
State string
|
||||
Error error
|
||||
Done bool
|
||||
}
|
||||
|
||||
// Read every result from the refresh loop, waiting for a positive result.Done.
|
||||
resCh := make(chan Result, 1)
|
||||
// cancellation channel for the refresh loop
|
||||
cancelCh := make(chan struct{})
|
||||
|
||||
result := Result{}
|
||||
|
||||
go func() {
|
||||
defer close(resCh)
|
||||
|
||||
time.Sleep(conf.Delay)
|
||||
|
||||
// start with 0 delay for the first loop
|
||||
var wait time.Duration
|
||||
|
||||
for {
|
||||
// store the last result
|
||||
resCh <- result
|
||||
|
||||
// wait and watch for cancellation
|
||||
select {
|
||||
case <-cancelCh:
|
||||
return
|
||||
case <-time.After(wait):
|
||||
// first round had no wait
|
||||
if wait == 0 {
|
||||
wait = 100 * time.Millisecond
|
||||
}
|
||||
}
|
||||
|
||||
res, currentState, err := conf.Refresh()
|
||||
result = Result{
|
||||
Result: res,
|
||||
State: currentState,
|
||||
Error: err,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resCh <- result
|
||||
return
|
||||
}
|
||||
|
||||
// If we're waiting for the absence of a thing, then return
|
||||
if res == nil && len(conf.Target) == 0 {
|
||||
targetOccurence++
|
||||
if conf.ContinuousTargetOccurence == targetOccurence {
|
||||
result.Done = true
|
||||
resCh <- result
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
// If we didn't find the resource, check if we have been
|
||||
// not finding it for awhile, and if so, report an error.
|
||||
notfoundTick++
|
||||
if notfoundTick > conf.NotFoundChecks {
|
||||
result.Error = &NotFoundError{
|
||||
LastError: err,
|
||||
Retries: notfoundTick,
|
||||
}
|
||||
resCh <- result
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Reset the counter for when a resource isn't found
|
||||
notfoundTick = 0
|
||||
found := false
|
||||
|
||||
for _, allowed := range conf.Target {
|
||||
if currentState == allowed {
|
||||
found = true
|
||||
targetOccurence++
|
||||
if conf.ContinuousTargetOccurence == targetOccurence {
|
||||
result.Done = true
|
||||
resCh <- result
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for _, allowed := range conf.Pending {
|
||||
if currentState == allowed {
|
||||
found = true
|
||||
targetOccurence = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found && len(conf.Pending) > 0 {
|
||||
result.Error = &UnexpectedStateError{
|
||||
LastError: err,
|
||||
State: result.State,
|
||||
ExpectedState: conf.Target,
|
||||
}
|
||||
resCh <- result
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Wait between refreshes using exponential backoff, except when
|
||||
// waiting for the target state to reoccur.
|
||||
if targetOccurence == 0 {
|
||||
wait *= 2
|
||||
}
|
||||
|
||||
// If a poll interval has been specified, choose that interval.
|
||||
// Otherwise bound the default value.
|
||||
if conf.PollInterval > 0 && conf.PollInterval < 180*time.Second {
|
||||
wait = conf.PollInterval
|
||||
} else {
|
||||
if wait < conf.MinTimeout {
|
||||
wait = conf.MinTimeout
|
||||
} else if wait > 10*time.Second {
|
||||
wait = 10 * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] Waiting %s before next try", wait)
|
||||
}
|
||||
}()
|
||||
|
||||
// store the last value result from the refresh loop
|
||||
lastResult := Result{}
|
||||
|
||||
timeout := time.After(conf.Timeout)
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-resCh:
|
||||
// channel closed, so return the last result
|
||||
if !ok {
|
||||
return lastResult.Result, lastResult.Error
|
||||
}
|
||||
|
||||
// we reached the intended state
|
||||
if r.Done {
|
||||
return r.Result, r.Error
|
||||
}
|
||||
|
||||
// still waiting, store the last result
|
||||
lastResult = r
|
||||
|
||||
case <-timeout:
|
||||
log.Printf("[WARN] WaitForState timeout after %s", conf.Timeout)
|
||||
log.Printf("[WARN] WaitForState starting %s refresh grace period", refreshGracePeriod)
|
||||
|
||||
// cancel the goroutine and start our grace period timer
|
||||
close(cancelCh)
|
||||
timeout := time.After(refreshGracePeriod)
|
||||
|
||||
// we need a for loop and a label to break on, because we may have
|
||||
// an extra response value to read, but still want to wait for the
|
||||
// channel to close.
|
||||
forSelect:
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-resCh:
|
||||
if r.Done {
|
||||
// the last refresh loop reached the desired state
|
||||
return r.Result, r.Error
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// the goroutine returned
|
||||
break forSelect
|
||||
}
|
||||
|
||||
// target state not reached, save the result for the
|
||||
// TimeoutError and wait for the channel to close
|
||||
lastResult = r
|
||||
case <-timeout:
|
||||
log.Println("[ERROR] WaitForState exceeded refresh grace period")
|
||||
break forSelect
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &TimeoutError{
|
||||
LastError: lastResult.Error,
|
||||
LastState: lastResult.State,
|
||||
Timeout: conf.Timeout,
|
||||
ExpectedState: conf.Target,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// shimState takes a new *states.State and reverts it to a legacy state for the provider ACC tests
|
||||
func shimNewState(newState *states.State, providers map[string]terraform.ResourceProvider) (*terraform.State, error) {
|
||||
state := terraform.NewState()
|
||||
|
||||
// in the odd case of a nil state, let the helper packages handle it
|
||||
if newState == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, newMod := range newState.Modules {
|
||||
mod := state.AddModule(newMod.Addr)
|
||||
|
||||
for name, out := range newMod.OutputValues {
|
||||
outputType := ""
|
||||
val := hcl2shim.ConfigValueFromHCL2(out.Value)
|
||||
ty := out.Value.Type()
|
||||
switch {
|
||||
case ty == cty.String:
|
||||
outputType = "string"
|
||||
case ty.IsTupleType() || ty.IsListType():
|
||||
outputType = "list"
|
||||
case ty.IsMapType():
|
||||
outputType = "map"
|
||||
}
|
||||
|
||||
mod.Outputs[name] = &terraform.OutputState{
|
||||
Type: outputType,
|
||||
Value: val,
|
||||
Sensitive: out.Sensitive,
|
||||
}
|
||||
}
|
||||
|
||||
for _, res := range newMod.Resources {
|
||||
resType := res.Addr.Resource.Type
|
||||
providerType := res.ProviderConfig.Provider.Type
|
||||
|
||||
resource := getResource(providers, providerType, res.Addr.Resource)
|
||||
|
||||
for key, i := range res.Instances {
|
||||
resState := &terraform.ResourceState{
|
||||
Type: resType,
|
||||
Provider: legacyProviderConfigString(res.ProviderConfig),
|
||||
}
|
||||
|
||||
// We should always have a Current instance here, but be safe about checking.
|
||||
if i.Current != nil {
|
||||
flatmap, err := shimmedAttributes(i.Current, resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding state for %q: %s", resType, err)
|
||||
}
|
||||
|
||||
var meta map[string]interface{}
|
||||
if i.Current.Private != nil {
|
||||
err := json.Unmarshal(i.Current.Private, &meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resState.Primary = &terraform.InstanceState{
|
||||
ID: flatmap["id"],
|
||||
Attributes: flatmap,
|
||||
Tainted: i.Current.Status == states.ObjectTainted,
|
||||
Meta: meta,
|
||||
}
|
||||
|
||||
if i.Current.SchemaVersion != 0 {
|
||||
if resState.Primary.Meta == nil {
|
||||
resState.Primary.Meta = map[string]interface{}{}
|
||||
}
|
||||
resState.Primary.Meta["schema_version"] = i.Current.SchemaVersion
|
||||
}
|
||||
|
||||
// convert the indexes to the old style flapmap indexes
|
||||
idx := ""
|
||||
switch key.(type) {
|
||||
case addrs.IntKey:
|
||||
// don't add numeric index values to resources with a count of 0
|
||||
if len(res.Instances) > 1 {
|
||||
idx = fmt.Sprintf(".%d", key)
|
||||
}
|
||||
case addrs.StringKey:
|
||||
idx = "." + key.String()
|
||||
}
|
||||
|
||||
mod.Resources[res.Addr.Resource.String()+idx] = resState
|
||||
}
|
||||
|
||||
// add any deposed instances
|
||||
for _, dep := range i.Deposed {
|
||||
flatmap, err := shimmedAttributes(dep, resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding deposed state for %q: %s", resType, err)
|
||||
}
|
||||
|
||||
var meta map[string]interface{}
|
||||
if dep.Private != nil {
|
||||
err := json.Unmarshal(dep.Private, &meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
deposed := &terraform.InstanceState{
|
||||
ID: flatmap["id"],
|
||||
Attributes: flatmap,
|
||||
Tainted: dep.Status == states.ObjectTainted,
|
||||
Meta: meta,
|
||||
}
|
||||
if dep.SchemaVersion != 0 {
|
||||
deposed.Meta = map[string]interface{}{
|
||||
"schema_version": dep.SchemaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
resState.Deposed = append(resState.Deposed, deposed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func getResource(providers map[string]terraform.ResourceProvider, providerName string, addr addrs.Resource) *schema.Resource {
|
||||
p := providers[providerName]
|
||||
if p == nil {
|
||||
panic(fmt.Sprintf("provider %q not found in test step", providerName))
|
||||
}
|
||||
|
||||
// this is only for tests, so should only see schema.Providers
|
||||
provider := p.(*schema.Provider)
|
||||
|
||||
switch addr.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
resource := provider.ResourcesMap[addr.Type]
|
||||
if resource != nil {
|
||||
return resource
|
||||
}
|
||||
case addrs.DataResourceMode:
|
||||
resource := provider.DataSourcesMap[addr.Type]
|
||||
if resource != nil {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("resource %s not found in test step", addr.Type))
|
||||
}
|
||||
|
||||
func shimmedAttributes(instance *states.ResourceInstanceObjectSrc, res *schema.Resource) (map[string]string, error) {
|
||||
flatmap := instance.AttrsFlat
|
||||
if flatmap != nil {
|
||||
return flatmap, nil
|
||||
}
|
||||
|
||||
// if we have json attrs, they need to be decoded
|
||||
rio, err := instance.Decode(res.CoreConfigSchema().ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceState, err := res.ShimInstanceStateFromValue(rio.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return instanceState.Attributes, nil
|
||||
}
|
||||
|
||||
func shimLegacyState(legacy *terraform.State) (*states.State, error) {
|
||||
state, err := terraform.ShimLegacyState(legacy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if state.HasResources() {
|
||||
for _, module := range state.Modules {
|
||||
for name, resource := range module.Resources {
|
||||
module.Resources[name].ProviderConfig.Provider = addrs.ImpliedProviderForUnqualifiedType(resource.Addr.Resource.ImpliedProvider())
|
||||
}
|
||||
}
|
||||
}
|
||||
return state, err
|
||||
}
|
||||
|
||||
// legacyProviderConfigString was copied from addrs.Provider.LegacyString() to
|
||||
// create a legacy-style string from a non-legacy provider. This is only
|
||||
// necessary as this package shims back and forth between legacy and modern
|
||||
// state, neither of which encode the addrs.Provider for a resource.
|
||||
func legacyProviderConfigString(pc addrs.AbsProviderConfig) string {
|
||||
if pc.Alias != "" {
|
||||
if len(pc.Module) == 0 {
|
||||
return fmt.Sprintf("%s.%s.%s", "provider", pc.Provider.Type, pc.Alias)
|
||||
} else {
|
||||
return fmt.Sprintf("%s.%s.%s.%s", pc.Module.String(), "provider", pc.Provider.LegacyString(), pc.Alias)
|
||||
}
|
||||
}
|
||||
if len(pc.Module) == 0 {
|
||||
return fmt.Sprintf("%s.%s", "provider", pc.Provider.Type)
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.%s", pc.Module.String(), "provider", pc.Provider.Type)
|
||||
}
|
|
@ -1,387 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// TestStateShim is meant to be a fairly comprehensive test, checking for dependencies, root outputs,
|
||||
func TestStateShim(t *testing.T) {
|
||||
state := states.NewState()
|
||||
|
||||
rootModule := state.RootModule()
|
||||
if rootModule == nil {
|
||||
t.Errorf("root module is nil; want valid object")
|
||||
}
|
||||
|
||||
rootModule.SetOutputValue("bar", cty.ListVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("value")}), false)
|
||||
rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true)
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "foo", "bazzle": "dazzle"},
|
||||
SchemaVersion: 7,
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "baz", "bazzle": "dazzle"},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
|
||||
childInstance := addrs.RootModuleInstance.Child("child", addrs.NoKey)
|
||||
childModule := state.EnsureModule(childInstance)
|
||||
childModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.DataResourceMode,
|
||||
Type: "test_data_thing",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id": "bar", "fuzzle":"wuzzle"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
childModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id": "bar", "fizzle":"wizzle"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
childModule.SetResourceInstanceDeposed(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.NoKey),
|
||||
"00000001",
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "old", "fizzle": "wizzle"},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
childModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "lots",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "0", "bazzle": "dazzle"},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
childModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "lots",
|
||||
}.Instance(addrs.IntKey(1)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectTainted,
|
||||
AttrsFlat: map[string]string{"id": "1", "bazzle": "dazzle"},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
childModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "single_count",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id": "single", "bazzle":"dazzle"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: childInstance.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
expected := &terraform.State{
|
||||
Version: 3,
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Outputs: map[string]*terraform.OutputState{
|
||||
"bar": {
|
||||
Type: "list",
|
||||
Value: []interface{}{"bar", "value"},
|
||||
},
|
||||
"secret": {
|
||||
Sensitive: true,
|
||||
Type: "string",
|
||||
Value: "secret value",
|
||||
},
|
||||
},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.baz": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "baz",
|
||||
Attributes: map[string]string{
|
||||
"id": "baz",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
"test_thing.foo": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Attributes: map[string]string{
|
||||
"id": "foo",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
Meta: map[string]interface{}{
|
||||
"schema_version": 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root", "child"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.baz": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
"fizzle": "wizzle",
|
||||
},
|
||||
},
|
||||
Deposed: []*terraform.InstanceState{
|
||||
{
|
||||
ID: "old",
|
||||
Attributes: map[string]string{
|
||||
"id": "old",
|
||||
"fizzle": "wizzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"data.test_data_thing.foo": &terraform.ResourceState{
|
||||
Type: "test_data_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
"fuzzle": "wuzzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
"test_thing.lots.0": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "0",
|
||||
Attributes: map[string]string{
|
||||
"id": "0",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
"test_thing.lots.1": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "1",
|
||||
Attributes: map[string]string{
|
||||
"id": "1",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
Tainted: true,
|
||||
},
|
||||
},
|
||||
"test_thing.single_count": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "single",
|
||||
Attributes: map[string]string{
|
||||
"id": "single",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
providers := map[string]terraform.ResourceProvider{
|
||||
"test": &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"test_thing": &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"id": {Type: schema.TypeString, Computed: true},
|
||||
"fizzle": {Type: schema.TypeString, Optional: true},
|
||||
"bazzle": {Type: schema.TypeString, Optional: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"test_data_thing": &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"id": {Type: schema.TypeString, Computed: true},
|
||||
"fuzzle": {Type: schema.TypeString, Optional: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
shimmed, err := shimNewState(state, providers)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !expected.Equal(shimmed) {
|
||||
t.Fatalf("wrong result state\ngot:\n%s\n\nwant:\n%s", shimmed, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestShimLegacyState only checks the functionality unique to this func: adding
|
||||
// the implied provider FQN
|
||||
func TestShimLegacyState(t *testing.T) {
|
||||
|
||||
input := &terraform.State{
|
||||
Version: 3,
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.baz": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "baz",
|
||||
Attributes: map[string]string{
|
||||
"id": "baz",
|
||||
"bazzle": "dazzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root", "child"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_thing.bar": &terraform.ResourceState{
|
||||
Type: "test_thing",
|
||||
Provider: "module.child.provider.test",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
"fizzle": "wizzle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := states.NewState()
|
||||
root := expected.EnsureModule(addrs.RootModuleInstance)
|
||||
root.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "baz", "bazzle": "dazzle"},
|
||||
Dependencies: []addrs.ConfigResource{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
child := expected.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
||||
child.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: "bar",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsFlat: map[string]string{"id": "bar", "fizzle": "wizzle"},
|
||||
Dependencies: []addrs.ConfigResource{},
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: child.Addr.Module(),
|
||||
},
|
||||
)
|
||||
|
||||
got, err := shimLegacyState(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if !got.Equal(expected) {
|
||||
t.Fatal("wrong result")
|
||||
}
|
||||
}
|
|
@ -1,329 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func FailedStateRefreshFunc() StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
return nil, "", errors.New("failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TimeoutStateRefreshFunc() StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
time.Sleep(100 * time.Second)
|
||||
return nil, "", errors.New("failed")
|
||||
}
|
||||
}
|
||||
|
||||
func SuccessfulStateRefreshFunc() StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
return struct{}{}, "running", nil
|
||||
}
|
||||
}
|
||||
|
||||
type StateGenerator struct {
|
||||
position int
|
||||
stateSequence []string
|
||||
}
|
||||
|
||||
func (r *StateGenerator) NextState() (int, string, error) {
|
||||
p, v := r.position, ""
|
||||
if len(r.stateSequence)-1 >= p {
|
||||
v = r.stateSequence[p]
|
||||
} else {
|
||||
return -1, "", errors.New("No more states available")
|
||||
}
|
||||
|
||||
r.position += 1
|
||||
|
||||
return p, v, nil
|
||||
}
|
||||
|
||||
func NewStateGenerator(sequence []string) *StateGenerator {
|
||||
r := &StateGenerator{}
|
||||
r.stateSequence = sequence
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func InconsistentStateRefreshFunc() StateRefreshFunc {
|
||||
sequence := []string{
|
||||
"done", "replicating",
|
||||
"done", "done", "done",
|
||||
"replicating",
|
||||
"done", "done", "done",
|
||||
}
|
||||
|
||||
r := NewStateGenerator(sequence)
|
||||
|
||||
return func() (interface{}, string, error) {
|
||||
idx, s, err := r.NextState()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return idx, s, nil
|
||||
}
|
||||
}
|
||||
|
||||
func UnknownPendingStateRefreshFunc() StateRefreshFunc {
|
||||
sequence := []string{
|
||||
"unknown1", "unknown2", "done",
|
||||
}
|
||||
|
||||
r := NewStateGenerator(sequence)
|
||||
|
||||
return func() (interface{}, string, error) {
|
||||
idx, s, err := r.NextState()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return idx, s, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_inconsistent_positive(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"replicating"},
|
||||
Target: []string{"done"},
|
||||
Refresh: InconsistentStateRefreshFunc(),
|
||||
Timeout: 90 * time.Millisecond,
|
||||
PollInterval: 10 * time.Millisecond,
|
||||
ContinuousTargetOccurence: 3,
|
||||
}
|
||||
|
||||
idx, err := conf.WaitForState()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if idx != 4 {
|
||||
t.Fatalf("Expected index 4, given %d", idx.(int))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_inconsistent_negative(t *testing.T) {
|
||||
refreshCount := int64(0)
|
||||
f := InconsistentStateRefreshFunc()
|
||||
refresh := func() (interface{}, string, error) {
|
||||
atomic.AddInt64(&refreshCount, 1)
|
||||
return f()
|
||||
}
|
||||
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"replicating"},
|
||||
Target: []string{"done"},
|
||||
Refresh: refresh,
|
||||
Timeout: 85 * time.Millisecond,
|
||||
PollInterval: 10 * time.Millisecond,
|
||||
ContinuousTargetOccurence: 4,
|
||||
}
|
||||
|
||||
_, err := conf.WaitForState()
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected timeout error. No error returned.")
|
||||
}
|
||||
|
||||
// we can't guarantee the exact number of refresh calls in the tests by
|
||||
// timing them, but we want to make sure the test at least went through th
|
||||
// required states.
|
||||
if atomic.LoadInt64(&refreshCount) < 6 {
|
||||
t.Fatal("refreshed called too few times")
|
||||
}
|
||||
|
||||
expectedErr := "timeout while waiting for state to become 'done'"
|
||||
if !strings.HasPrefix(err.Error(), expectedErr) {
|
||||
t.Fatalf("error prefix doesn't match.\nExpected: %q\nGiven: %q\n", expectedErr, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_timeout(t *testing.T) {
|
||||
old := refreshGracePeriod
|
||||
refreshGracePeriod = 5 * time.Millisecond
|
||||
defer func() {
|
||||
refreshGracePeriod = old
|
||||
}()
|
||||
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{"running"},
|
||||
Refresh: TimeoutStateRefreshFunc(),
|
||||
Timeout: 1 * time.Millisecond,
|
||||
}
|
||||
|
||||
obj, err := conf.WaitForState()
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected timeout error. No error returned.")
|
||||
}
|
||||
|
||||
expectedErr := "timeout while waiting for state to become 'running' (timeout: 1ms)"
|
||||
if err.Error() != expectedErr {
|
||||
t.Fatalf("Errors don't match.\nExpected: %q\nGiven: %q\n", expectedErr, err.Error())
|
||||
}
|
||||
|
||||
if obj != nil {
|
||||
t.Fatalf("should not return obj")
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure a timeout actually cancels the refresh goroutine and waits for its
|
||||
// return.
|
||||
func TestWaitForState_cancel(t *testing.T) {
|
||||
// make this refresh func block until we cancel it
|
||||
cancel := make(chan struct{})
|
||||
refresh := func() (interface{}, string, error) {
|
||||
<-cancel
|
||||
return nil, "pending", nil
|
||||
}
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{"running"},
|
||||
Refresh: refresh,
|
||||
Timeout: 10 * time.Millisecond,
|
||||
PollInterval: 10 * time.Second,
|
||||
}
|
||||
|
||||
var obj interface{}
|
||||
var err error
|
||||
|
||||
waitDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(waitDone)
|
||||
obj, err = conf.WaitForState()
|
||||
}()
|
||||
|
||||
// make sure WaitForState is blocked
|
||||
select {
|
||||
case <-waitDone:
|
||||
t.Fatal("WaitForState returned too early")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
// unlock the refresh function
|
||||
close(cancel)
|
||||
// make sure WaitForState returns
|
||||
select {
|
||||
case <-waitDone:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("WaitForState didn't return after refresh finished")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected timeout error. No error returned.")
|
||||
}
|
||||
|
||||
expectedErr := "timeout while waiting for state to become 'running'"
|
||||
if !strings.HasPrefix(err.Error(), expectedErr) {
|
||||
t.Fatalf("Errors don't match.\nExpected: %q\nGiven: %q\n", expectedErr, err.Error())
|
||||
}
|
||||
|
||||
if obj != nil {
|
||||
t.Fatalf("should not return obj")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestWaitForState_success(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{"running"},
|
||||
Refresh: SuccessfulStateRefreshFunc(),
|
||||
Timeout: 200 * time.Second,
|
||||
}
|
||||
|
||||
obj, err := conf.WaitForState()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if obj == nil {
|
||||
t.Fatalf("should return obj")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_successUnknownPending(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Target: []string{"done"},
|
||||
Refresh: UnknownPendingStateRefreshFunc(),
|
||||
Timeout: 200 * time.Second,
|
||||
}
|
||||
|
||||
obj, err := conf.WaitForState()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if obj == nil {
|
||||
t.Fatalf("should return obj")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_successEmpty(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{},
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
return nil, "", nil
|
||||
},
|
||||
Timeout: 200 * time.Second,
|
||||
}
|
||||
|
||||
obj, err := conf.WaitForState()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if obj != nil {
|
||||
t.Fatalf("obj should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_failureEmpty(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{},
|
||||
NotFoundChecks: 1,
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
return 42, "pending", nil
|
||||
},
|
||||
PollInterval: 10 * time.Millisecond,
|
||||
Timeout: 100 * time.Millisecond,
|
||||
}
|
||||
|
||||
_, err := conf.WaitForState()
|
||||
if err == nil {
|
||||
t.Fatal("Expected timeout error. Got none.")
|
||||
}
|
||||
expectedErr := "timeout while waiting for resource to be gone (last state: 'pending', timeout: 100ms)"
|
||||
if err.Error() != expectedErr {
|
||||
t.Fatalf("Errors don't match.\nExpected: %q\nGiven: %q\n", expectedErr, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForState_failure(t *testing.T) {
|
||||
conf := &StateChangeConf{
|
||||
Pending: []string{"pending", "incomplete"},
|
||||
Target: []string{"running"},
|
||||
Refresh: FailedStateRefreshFunc(),
|
||||
Timeout: 200 * time.Second,
|
||||
}
|
||||
|
||||
obj, err := conf.WaitForState()
|
||||
if err == nil {
|
||||
t.Fatal("Expected error. No error returned.")
|
||||
}
|
||||
expectedErr := "failed"
|
||||
if err.Error() != expectedErr {
|
||||
t.Fatalf("Errors don't match.\nExpected: %q\nGiven: %q\n", expectedErr, err.Error())
|
||||
}
|
||||
if obj != nil {
|
||||
t.Fatalf("should not return obj")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,378 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// testStepConfig runs a config-mode test step
|
||||
func testStepConfig(
|
||||
opts terraform.ContextOpts,
|
||||
state *terraform.State,
|
||||
step TestStep) (*terraform.State, error) {
|
||||
return testStep(opts, state, step)
|
||||
}
|
||||
|
||||
func testStep(opts terraform.ContextOpts, state *terraform.State, step TestStep) (*terraform.State, error) {
|
||||
if !step.Destroy {
|
||||
if err := testStepTaint(state, step); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := testConfig(opts, step)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
var stepDiags tfdiags.Diagnostics
|
||||
|
||||
// Build the context
|
||||
opts.Config = cfg
|
||||
opts.State, err = shimLegacyState(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts.Destroy = step.Destroy
|
||||
ctx, stepDiags := terraform.NewContext(&opts)
|
||||
if stepDiags.HasErrors() {
|
||||
return state, fmt.Errorf("Error initializing context: %s", stepDiags.Err())
|
||||
}
|
||||
if stepDiags := ctx.Validate(); len(stepDiags) > 0 {
|
||||
if stepDiags.HasErrors() {
|
||||
return state, errwrap.Wrapf("config is invalid: {{err}}", stepDiags.Err())
|
||||
}
|
||||
|
||||
log.Printf("[WARN] Config warnings:\n%s", stepDiags)
|
||||
}
|
||||
|
||||
// If this step is a PlanOnly step, skip over this first Plan and subsequent
|
||||
// Apply, and use the follow up Plan that checks for perpetual diffs
|
||||
if !step.PlanOnly {
|
||||
// Plan!
|
||||
p, stepDiags := ctx.Plan()
|
||||
if stepDiags.HasErrors() {
|
||||
return state, newOperationError("plan", stepDiags)
|
||||
}
|
||||
|
||||
newState := p.State
|
||||
log.Printf("[WARN] Test: Step plan: %s", legacyPlanComparisonString(newState, p.Changes))
|
||||
|
||||
// We need to keep a copy of the state prior to destroying
|
||||
// such that destroy steps can verify their behavior in the check
|
||||
// function
|
||||
stateBeforeApplication := state.DeepCopy()
|
||||
|
||||
// Apply the diff, creating real resources.
|
||||
newState, stepDiags = ctx.Apply()
|
||||
// shim the state first so the test can check the state on errors
|
||||
state, err = shimNewState(newState, step.providers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stepDiags.HasErrors() {
|
||||
return state, newOperationError("apply", stepDiags)
|
||||
}
|
||||
|
||||
// Run any configured checks
|
||||
if step.Check != nil {
|
||||
if step.Destroy {
|
||||
if err := step.Check(stateBeforeApplication); err != nil {
|
||||
return state, fmt.Errorf("Check failed: %s", err)
|
||||
}
|
||||
} else {
|
||||
if err := step.Check(state); err != nil {
|
||||
return state, fmt.Errorf("Check failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, verify that Plan is now empty and we don't have a perpetual diff issue
|
||||
// We do this with TWO plans. One without a refresh.
|
||||
p, stepDiags := ctx.Plan()
|
||||
if stepDiags.HasErrors() {
|
||||
return state, newOperationError("follow-up plan", stepDiags)
|
||||
}
|
||||
|
||||
// we don't technically need this any longer with plan handling refreshing,
|
||||
// but run it anyway to ensure the context is working as expected.
|
||||
p, stepDiags = ctx.Plan()
|
||||
if stepDiags.HasErrors() {
|
||||
return state, newOperationError("second follow-up plan", stepDiags)
|
||||
}
|
||||
empty := true
|
||||
newState := p.State
|
||||
|
||||
// the legacy tests never took outputs into account
|
||||
for _, c := range p.Changes.Resources {
|
||||
if c.Action != plans.NoOp {
|
||||
empty = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !empty {
|
||||
if step.ExpectNonEmptyPlan {
|
||||
log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", legacyPlanComparisonString(newState, p.Changes))
|
||||
} else {
|
||||
return state, fmt.Errorf(
|
||||
"After applying this step, the plan was not empty:\n\n%s", legacyPlanComparisonString(newState, p.Changes))
|
||||
}
|
||||
}
|
||||
|
||||
if !empty {
|
||||
if step.ExpectNonEmptyPlan {
|
||||
log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", legacyPlanComparisonString(newState, p.Changes))
|
||||
} else {
|
||||
return state, fmt.Errorf(
|
||||
"After applying this step and refreshing, "+
|
||||
"the plan was not empty:\n\n%s", legacyPlanComparisonString(newState, p.Changes))
|
||||
}
|
||||
}
|
||||
|
||||
// Made it here, but expected a non-empty plan, fail!
|
||||
if step.ExpectNonEmptyPlan && empty {
|
||||
return state, fmt.Errorf("Expected a non-empty plan, but got an empty plan!")
|
||||
}
|
||||
|
||||
// Made it here? Good job test step!
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// legacyPlanComparisonString produces a string representation of the changes
|
||||
// from a plan and a given state togther, as was formerly produced by the
|
||||
// String method of terraform.Plan.
|
||||
//
|
||||
// This is here only for compatibility with existing tests that predate our
|
||||
// new plan and state types, and should not be used in new tests. Instead, use
|
||||
// a library like "cmp" to do a deep equality and diff on the two
|
||||
// data structures.
|
||||
func legacyPlanComparisonString(state *states.State, changes *plans.Changes) string {
|
||||
return fmt.Sprintf(
|
||||
"DIFF:\n\n%s\n\nSTATE:\n\n%s",
|
||||
legacyDiffComparisonString(changes),
|
||||
state.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// legacyDiffComparisonString produces a string representation of the changes
|
||||
// from a planned changes object, as was formerly produced by the String method
|
||||
// of terraform.Diff.
|
||||
//
|
||||
// This is here only for compatibility with existing tests that predate our
|
||||
// new plan types, and should not be used in new tests. Instead, use a library
|
||||
// like "cmp" to do a deep equality check and diff on the two data structures.
|
||||
func legacyDiffComparisonString(changes *plans.Changes) string {
|
||||
// The old string representation of a plan was grouped by module, but
|
||||
// our new plan structure is not grouped in that way and so we'll need
|
||||
// to preprocess it in order to produce that grouping.
|
||||
type ResourceChanges struct {
|
||||
Current *plans.ResourceInstanceChangeSrc
|
||||
Deposed map[states.DeposedKey]*plans.ResourceInstanceChangeSrc
|
||||
}
|
||||
byModule := map[string]map[string]*ResourceChanges{}
|
||||
resourceKeys := map[string][]string{}
|
||||
requiresReplace := map[string][]string{}
|
||||
var moduleKeys []string
|
||||
for _, rc := range changes.Resources {
|
||||
if rc.Action == plans.NoOp {
|
||||
// We won't mention no-op changes here at all, since the old plan
|
||||
// model we are emulating here didn't have such a concept.
|
||||
continue
|
||||
}
|
||||
moduleKey := rc.Addr.Module.String()
|
||||
if _, exists := byModule[moduleKey]; !exists {
|
||||
moduleKeys = append(moduleKeys, moduleKey)
|
||||
byModule[moduleKey] = make(map[string]*ResourceChanges)
|
||||
}
|
||||
resourceKey := rc.Addr.Resource.String()
|
||||
if _, exists := byModule[moduleKey][resourceKey]; !exists {
|
||||
resourceKeys[moduleKey] = append(resourceKeys[moduleKey], resourceKey)
|
||||
byModule[moduleKey][resourceKey] = &ResourceChanges{
|
||||
Deposed: make(map[states.DeposedKey]*plans.ResourceInstanceChangeSrc),
|
||||
}
|
||||
}
|
||||
|
||||
if rc.DeposedKey == states.NotDeposed {
|
||||
byModule[moduleKey][resourceKey].Current = rc
|
||||
} else {
|
||||
byModule[moduleKey][resourceKey].Deposed[rc.DeposedKey] = rc
|
||||
}
|
||||
|
||||
rr := []string{}
|
||||
for _, p := range rc.RequiredReplace.List() {
|
||||
rr = append(rr, hcl2shim.FlatmapKeyFromPath(p))
|
||||
}
|
||||
requiresReplace[resourceKey] = rr
|
||||
}
|
||||
sort.Strings(moduleKeys)
|
||||
for _, ks := range resourceKeys {
|
||||
sort.Strings(ks)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, moduleKey := range moduleKeys {
|
||||
rcs := byModule[moduleKey]
|
||||
var mBuf bytes.Buffer
|
||||
|
||||
for _, resourceKey := range resourceKeys[moduleKey] {
|
||||
rc := rcs[resourceKey]
|
||||
|
||||
forceNewAttrs := requiresReplace[resourceKey]
|
||||
|
||||
crud := "UPDATE"
|
||||
if rc.Current != nil {
|
||||
switch rc.Current.Action {
|
||||
case plans.DeleteThenCreate:
|
||||
crud = "DESTROY/CREATE"
|
||||
case plans.CreateThenDelete:
|
||||
crud = "CREATE/DESTROY"
|
||||
case plans.Delete:
|
||||
crud = "DESTROY"
|
||||
case plans.Create:
|
||||
crud = "CREATE"
|
||||
}
|
||||
} else {
|
||||
// We must be working on a deposed object then, in which
|
||||
// case destroying is the only possible action.
|
||||
crud = "DESTROY"
|
||||
}
|
||||
|
||||
extra := ""
|
||||
if rc.Current == nil && len(rc.Deposed) > 0 {
|
||||
extra = " (deposed only)"
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
&mBuf, "%s: %s%s\n",
|
||||
crud, resourceKey, extra,
|
||||
)
|
||||
|
||||
attrNames := map[string]bool{}
|
||||
var oldAttrs map[string]string
|
||||
var newAttrs map[string]string
|
||||
if rc.Current != nil {
|
||||
if before := rc.Current.Before; before != nil {
|
||||
ty, err := before.ImpliedType()
|
||||
if err == nil {
|
||||
val, err := before.Decode(ty)
|
||||
if err == nil {
|
||||
oldAttrs = hcl2shim.FlatmapValueFromHCL2(val)
|
||||
for k := range oldAttrs {
|
||||
attrNames[k] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if after := rc.Current.After; after != nil {
|
||||
ty, err := after.ImpliedType()
|
||||
if err == nil {
|
||||
val, err := after.Decode(ty)
|
||||
if err == nil {
|
||||
newAttrs = hcl2shim.FlatmapValueFromHCL2(val)
|
||||
for k := range newAttrs {
|
||||
attrNames[k] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if oldAttrs == nil {
|
||||
oldAttrs = make(map[string]string)
|
||||
}
|
||||
if newAttrs == nil {
|
||||
newAttrs = make(map[string]string)
|
||||
}
|
||||
|
||||
attrNamesOrder := make([]string, 0, len(attrNames))
|
||||
keyLen := 0
|
||||
for n := range attrNames {
|
||||
attrNamesOrder = append(attrNamesOrder, n)
|
||||
if len(n) > keyLen {
|
||||
keyLen = len(n)
|
||||
}
|
||||
}
|
||||
sort.Strings(attrNamesOrder)
|
||||
|
||||
for _, attrK := range attrNamesOrder {
|
||||
v := newAttrs[attrK]
|
||||
u := oldAttrs[attrK]
|
||||
|
||||
if v == hcl2shim.UnknownVariableValue {
|
||||
v = "<computed>"
|
||||
}
|
||||
// NOTE: we don't support <sensitive> here because we would
|
||||
// need schema to do that. Excluding sensitive values
|
||||
// is now done at the UI layer, and so should not be tested
|
||||
// at the core layer.
|
||||
|
||||
updateMsg := ""
|
||||
|
||||
// This may not be as precise as in the old diff, as it matches
|
||||
// everything under the attribute that was originally marked as
|
||||
// ForceNew, but should help make it easier to determine what
|
||||
// caused replacement here.
|
||||
for _, k := range forceNewAttrs {
|
||||
if strings.HasPrefix(attrK, k) {
|
||||
updateMsg = " (forces new resource)"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
&mBuf, " %s:%s %#v => %#v%s\n",
|
||||
attrK,
|
||||
strings.Repeat(" ", keyLen-len(attrK)),
|
||||
u, v,
|
||||
updateMsg,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if moduleKey == "" { // root module
|
||||
buf.Write(mBuf.Bytes())
|
||||
buf.WriteByte('\n')
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(&buf, "%s:\n", moduleKey)
|
||||
s := bufio.NewScanner(&mBuf)
|
||||
for s.Scan() {
|
||||
buf.WriteString(fmt.Sprintf(" %s\n", s.Text()))
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func testStepTaint(state *terraform.State, step TestStep) error {
|
||||
for _, p := range step.Taint {
|
||||
m := state.RootModule()
|
||||
if m == nil {
|
||||
return errors.New("no state")
|
||||
}
|
||||
rs, ok := m.Resources[p]
|
||||
if !ok {
|
||||
return fmt.Errorf("resource %q not found in state", p)
|
||||
}
|
||||
log.Printf("[WARN] Test: Explicitly tainting resource %q", p)
|
||||
rs.Taint()
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// testStepImportState runs an import state test step
|
||||
func testStepImportState(
|
||||
opts terraform.ContextOpts,
|
||||
state *terraform.State,
|
||||
step TestStep) (*terraform.State, error) {
|
||||
|
||||
// Determine the ID to import
|
||||
var importId string
|
||||
switch {
|
||||
case step.ImportStateIdFunc != nil:
|
||||
var err error
|
||||
importId, err = step.ImportStateIdFunc(state)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
case step.ImportStateId != "":
|
||||
importId = step.ImportStateId
|
||||
default:
|
||||
resource, err := testResource(step, state)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
importId = resource.Primary.ID
|
||||
}
|
||||
|
||||
importPrefix := step.ImportStateIdPrefix
|
||||
if importPrefix != "" {
|
||||
importId = fmt.Sprintf("%s%s", importPrefix, importId)
|
||||
}
|
||||
|
||||
// Setup the context. We initialize with an empty state. We use the
|
||||
// full config for provider configurations.
|
||||
cfg, err := testConfig(opts, step)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
opts.Config = cfg
|
||||
|
||||
// import tests start with empty state
|
||||
opts.State = states.NewState()
|
||||
|
||||
ctx, stepDiags := terraform.NewContext(&opts)
|
||||
if stepDiags.HasErrors() {
|
||||
return state, stepDiags.Err()
|
||||
}
|
||||
|
||||
// The test step provides the resource address as a string, so we need
|
||||
// to parse it to get an addrs.AbsResourceAddress to pass in to the
|
||||
// import method.
|
||||
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(step.ResourceName), "", hcl.Pos{})
|
||||
if hclDiags.HasErrors() {
|
||||
return nil, hclDiags
|
||||
}
|
||||
importAddr, stepDiags := addrs.ParseAbsResourceInstance(traversal)
|
||||
if stepDiags.HasErrors() {
|
||||
return nil, stepDiags.Err()
|
||||
}
|
||||
|
||||
// Do the import
|
||||
importedState, stepDiags := ctx.Import(&terraform.ImportOpts{
|
||||
Targets: []*terraform.ImportTarget{
|
||||
&terraform.ImportTarget{
|
||||
Addr: importAddr,
|
||||
ID: importId,
|
||||
},
|
||||
},
|
||||
})
|
||||
if stepDiags.HasErrors() {
|
||||
log.Printf("[ERROR] Test: ImportState failure: %s", stepDiags.Err())
|
||||
return state, stepDiags.Err()
|
||||
}
|
||||
|
||||
newState, err := shimNewState(importedState, step.providers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Go through the new state and verify
|
||||
if step.ImportStateCheck != nil {
|
||||
var states []*terraform.InstanceState
|
||||
for _, r := range newState.RootModule().Resources {
|
||||
if r.Primary != nil {
|
||||
is := r.Primary.DeepCopy()
|
||||
is.Ephemeral.Type = r.Type // otherwise the check function cannot see the type
|
||||
states = append(states, is)
|
||||
}
|
||||
}
|
||||
if err := step.ImportStateCheck(states); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all the states match
|
||||
if step.ImportStateVerify {
|
||||
new := newState.RootModule().Resources
|
||||
old := state.RootModule().Resources
|
||||
for _, r := range new {
|
||||
// Find the existing resource
|
||||
var oldR *terraform.ResourceState
|
||||
for _, r2 := range old {
|
||||
if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type {
|
||||
oldR = r2
|
||||
break
|
||||
}
|
||||
}
|
||||
if oldR == nil {
|
||||
return state, fmt.Errorf(
|
||||
"Failed state verification, resource with ID %s not found",
|
||||
r.Primary.ID)
|
||||
}
|
||||
|
||||
// We'll try our best to find the schema for this resource type
|
||||
// so we can ignore Removed fields during validation. If we fail
|
||||
// to find the schema then we won't ignore them and so the test
|
||||
// will need to rely on explicit ImportStateVerifyIgnore, though
|
||||
// this shouldn't happen in any reasonable case.
|
||||
var rsrcSchema *schema.Resource
|
||||
if providerAddr, diags := addrs.ParseAbsProviderConfigStr(r.Provider); !diags.HasErrors() {
|
||||
// FIXME
|
||||
providerType := providerAddr.Provider.Type
|
||||
if provider, ok := step.providers[providerType]; ok {
|
||||
if provider, ok := provider.(*schema.Provider); ok {
|
||||
rsrcSchema = provider.ResourcesMap[r.Type]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't add empty flatmapped containers, so we can more easily
|
||||
// compare the attributes
|
||||
skipEmpty := func(k, v string) bool {
|
||||
if strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%") {
|
||||
if v == "0" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare their attributes
|
||||
actual := make(map[string]string)
|
||||
for k, v := range r.Primary.Attributes {
|
||||
if skipEmpty(k, v) {
|
||||
continue
|
||||
}
|
||||
actual[k] = v
|
||||
}
|
||||
|
||||
expected := make(map[string]string)
|
||||
for k, v := range oldR.Primary.Attributes {
|
||||
if skipEmpty(k, v) {
|
||||
continue
|
||||
}
|
||||
expected[k] = v
|
||||
}
|
||||
|
||||
// Remove fields we're ignoring
|
||||
for _, v := range step.ImportStateVerifyIgnore {
|
||||
for k := range actual {
|
||||
if strings.HasPrefix(k, v) {
|
||||
delete(actual, k)
|
||||
}
|
||||
}
|
||||
for k := range expected {
|
||||
if strings.HasPrefix(k, v) {
|
||||
delete(expected, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also remove any attributes that are marked as "Removed" in the
|
||||
// schema, if we have a schema to check that against.
|
||||
if rsrcSchema != nil {
|
||||
for k := range actual {
|
||||
for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
|
||||
if schema.Removed != "" {
|
||||
delete(actual, k)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for k := range expected {
|
||||
for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
|
||||
if schema.Removed != "" {
|
||||
delete(expected, k)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
// Determine only the different attributes
|
||||
for k, v := range expected {
|
||||
if av, ok := actual[k]; ok && v == av {
|
||||
delete(expected, k)
|
||||
delete(actual, k)
|
||||
}
|
||||
}
|
||||
|
||||
spewConf := spew.NewDefaultConfig()
|
||||
spewConf.SortKeys = true
|
||||
return state, fmt.Errorf(
|
||||
"ImportStateVerify attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
|
||||
"\n\n%s\n\n%s",
|
||||
spewConf.Sdump(actual), spewConf.Sdump(expected))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the old state (non-imported) so we don't change anything.
|
||||
return state, nil
|
||||
}
|
|
@ -1,517 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestTest_importState(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.ImportStateReturn = []*terraform.InstanceState{
|
||||
&terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
checked := false
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
checked = true
|
||||
|
||||
if s[0].ID != "foo" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStrProvider,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateId: "foo",
|
||||
ImportStateCheck: checkFn,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if mt.failed() {
|
||||
t.Fatalf("test failed: %s", mt.failMessage())
|
||||
}
|
||||
if !checked {
|
||||
t.Fatal("didn't call check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateFail(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.ImportStateReturn = []*terraform.InstanceState{
|
||||
&terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
checked := false
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
checked = true
|
||||
|
||||
if s[0].ID != "foo" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStrProvider,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateId: "foo",
|
||||
ImportStateCheck: checkFn,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !mt.failed() {
|
||||
t.Fatal("should fail")
|
||||
}
|
||||
if !checked {
|
||||
t.Fatal("didn't call check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateDetectId(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.DiffReturn = nil
|
||||
mp.ApplyFn = func(
|
||||
info *terraform.InstanceInfo,
|
||||
state *terraform.InstanceState,
|
||||
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
||||
if !diff.Destroy {
|
||||
return &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "foo" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
&terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
checked := false
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
checked = true
|
||||
|
||||
if s[0].ID != "bar" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
},
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateCheck: checkFn,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if mt.failed() {
|
||||
t.Fatalf("test failed: %s", mt.failMessage())
|
||||
}
|
||||
if !checked {
|
||||
t.Fatal("didn't call check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateIdPrefix(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.DiffReturn = nil
|
||||
mp.ApplyFn = func(
|
||||
info *terraform.InstanceInfo,
|
||||
state *terraform.InstanceState,
|
||||
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
||||
if !diff.Destroy {
|
||||
return &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "bazfoo" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
{
|
||||
ID: "bar",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
checked := false
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
checked = true
|
||||
|
||||
if s[0].ID != "bar" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
{
|
||||
Config: testConfigStr,
|
||||
},
|
||||
{
|
||||
Config: testConfigStr,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateCheck: checkFn,
|
||||
ImportStateIdPrefix: "baz",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if mt.failed() {
|
||||
t.Fatalf("test failed: %s", mt.failMessage())
|
||||
}
|
||||
if !checked {
|
||||
t.Fatal("didn't call check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateVerify(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.DiffReturn = nil
|
||||
mp.ApplyFn = func(
|
||||
info *terraform.InstanceInfo,
|
||||
state *terraform.InstanceState,
|
||||
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
||||
if !diff.Destroy {
|
||||
return &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Attributes: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
if len(s.Attributes) == 0 {
|
||||
s.Attributes = map[string]string{
|
||||
"id": s.ID,
|
||||
"foo": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "foo" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
&terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
},
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if mt.failed() {
|
||||
t.Fatalf("test failed: %s", mt.failMessage())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateVerifyFail(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.DiffReturn = nil
|
||||
mp.ApplyFn = func(
|
||||
info *terraform.InstanceInfo,
|
||||
state *terraform.InstanceState,
|
||||
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
||||
if !diff.Destroy {
|
||||
return &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Attributes: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "foo" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
&terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
},
|
||||
TestStep{
|
||||
Config: testConfigStr,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !mt.failed() {
|
||||
t.Fatalf("test should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateIdFunc(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "foo:bar" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
{
|
||||
ID: "foo",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
checked := false
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
checked = true
|
||||
|
||||
if s[0].ID != "foo" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStrProvider,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateIdFunc: func(*terraform.State) (string, error) { return "foo:bar", nil },
|
||||
ImportStateCheck: checkFn,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if mt.failed() {
|
||||
t.Fatalf("test failed: %s", mt.failMessage())
|
||||
}
|
||||
if !checked {
|
||||
t.Fatal("didn't call check")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest_importStateIdFuncFail(t *testing.T) {
|
||||
t.Skip("test requires new provider implementation")
|
||||
|
||||
mp := testProvider()
|
||||
mp.ImportStateFn = func(
|
||||
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
|
||||
if id != "foo:bar" {
|
||||
return nil, fmt.Errorf("bad import ID: %s", id)
|
||||
}
|
||||
|
||||
return []*terraform.InstanceState{
|
||||
{
|
||||
ID: "foo",
|
||||
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
mp.RefreshFn = func(
|
||||
i *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
checkFn := func(s []*terraform.InstanceState) error {
|
||||
if s[0].ID != "foo" {
|
||||
return fmt.Errorf("bad: %#v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mt := new(mockT)
|
||||
Test(mt, TestCase{
|
||||
Providers: map[string]terraform.ResourceProvider{
|
||||
"test": mp,
|
||||
},
|
||||
|
||||
Steps: []TestStep{
|
||||
TestStep{
|
||||
Config: testConfigStrProvider,
|
||||
ResourceName: "test_instance.foo",
|
||||
ImportState: true,
|
||||
ImportStateIdFunc: func(*terraform.State) (string, error) { return "foo:bar", errors.New("foobar") },
|
||||
ImportStateCheck: checkFn,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !mt.failed() {
|
||||
t.Fatalf("test should fail")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,84 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retry is a basic wrapper around StateChangeConf that will just retry
|
||||
// a function until it no longer returns an error.
|
||||
func Retry(timeout time.Duration, f RetryFunc) error {
|
||||
// These are used to pull the error out of the function; need a mutex to
|
||||
// avoid a data race.
|
||||
var resultErr error
|
||||
var resultErrMu sync.Mutex
|
||||
|
||||
c := &StateChangeConf{
|
||||
Pending: []string{"retryableerror"},
|
||||
Target: []string{"success"},
|
||||
Timeout: timeout,
|
||||
MinTimeout: 500 * time.Millisecond,
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
rerr := f()
|
||||
|
||||
resultErrMu.Lock()
|
||||
defer resultErrMu.Unlock()
|
||||
|
||||
if rerr == nil {
|
||||
resultErr = nil
|
||||
return 42, "success", nil
|
||||
}
|
||||
|
||||
resultErr = rerr.Err
|
||||
|
||||
if rerr.Retryable {
|
||||
return 42, "retryableerror", nil
|
||||
}
|
||||
return nil, "quit", rerr.Err
|
||||
},
|
||||
}
|
||||
|
||||
_, waitErr := c.WaitForState()
|
||||
|
||||
// Need to acquire the lock here to be able to avoid race using resultErr as
|
||||
// the return value
|
||||
resultErrMu.Lock()
|
||||
defer resultErrMu.Unlock()
|
||||
|
||||
// resultErr may be nil because the wait timed out and resultErr was never
|
||||
// set; this is still an error
|
||||
if resultErr == nil {
|
||||
return waitErr
|
||||
}
|
||||
// resultErr takes precedence over waitErr if both are set because it is
|
||||
// more likely to be useful
|
||||
return resultErr
|
||||
}
|
||||
|
||||
// RetryFunc is the function retried until it succeeds.
|
||||
type RetryFunc func() *RetryError
|
||||
|
||||
// RetryError is the required return type of RetryFunc. It forces client code
|
||||
// to choose whether or not a given error is retryable.
|
||||
type RetryError struct {
|
||||
Err error
|
||||
Retryable bool
|
||||
}
|
||||
|
||||
// RetryableError is a helper to create a RetryError that's retryable from a
|
||||
// given error.
|
||||
func RetryableError(err error) *RetryError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &RetryError{Err: err, Retryable: true}
|
||||
}
|
||||
|
||||
// NonRetryableError is a helper to create a RetryError that's _not_ retryable
|
||||
// from a given error.
|
||||
func NonRetryableError(err error) *RetryError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &RetryError{Err: err, Retryable: false}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tries := 0
|
||||
f := func() *RetryError {
|
||||
tries++
|
||||
if tries == 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return RetryableError(fmt.Errorf("error"))
|
||||
}
|
||||
|
||||
err := Retry(10*time.Second, f)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// make sure a slow StateRefreshFunc is allowed to complete after timeout
|
||||
func TestRetry_grace(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := func() *RetryError {
|
||||
time.Sleep(1 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(10*time.Millisecond, f)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetry_timeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := func() *RetryError {
|
||||
return RetryableError(fmt.Errorf("always"))
|
||||
}
|
||||
|
||||
err := Retry(1*time.Second, f)
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetry_hang(t *testing.T) {
|
||||
old := refreshGracePeriod
|
||||
refreshGracePeriod = 50 * time.Millisecond
|
||||
defer func() {
|
||||
refreshGracePeriod = old
|
||||
}()
|
||||
|
||||
f := func() *RetryError {
|
||||
time.Sleep(2 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(50*time.Millisecond, f)
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetry_error(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := fmt.Errorf("nope")
|
||||
f := func() *RetryError {
|
||||
return NonRetryableError(expected)
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
errCh <- Retry(1*time.Second, f)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != expected {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timeout")
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// IntBetween returns a SchemaValidateFunc which tests if the provided value
|
||||
// is of type int and is between min and max (inclusive)
|
||||
func IntBetween(min, max int) schema.SchemaValidateFunc {
|
||||
return func(i interface{}, k string) (s []string, es []error) {
|
||||
v, ok := i.(int)
|
||||
if !ok {
|
||||
es = append(es, fmt.Errorf("expected type of %s to be int", k))
|
||||
return
|
||||
}
|
||||
|
||||
if v < min || v > max {
|
||||
es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v))
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// StringInSlice returns a SchemaValidateFunc which tests if the provided value
|
||||
// is of type string and matches the value of an element in the valid slice
|
||||
// will test with in lower case if ignoreCase is true
|
||||
func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc {
|
||||
return func(i interface{}, k string) (s []string, es []error) {
|
||||
v, ok := i.(string)
|
||||
if !ok {
|
||||
es = append(es, fmt.Errorf("expected type of %s to be string", k))
|
||||
return
|
||||
}
|
||||
|
||||
for _, str := range valid {
|
||||
if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v))
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package validation
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
val interface{}
|
||||
f schema.SchemaValidateFunc
|
||||
expectedErr *regexp.Regexp
|
||||
}
|
||||
|
||||
func TestValidationIntBetween(t *testing.T) {
|
||||
runTestCases(t, []testCase{
|
||||
{
|
||||
val: 1,
|
||||
f: IntBetween(1, 1),
|
||||
},
|
||||
{
|
||||
val: 1,
|
||||
f: IntBetween(0, 2),
|
||||
},
|
||||
{
|
||||
val: 1,
|
||||
f: IntBetween(2, 3),
|
||||
expectedErr: regexp.MustCompile("expected [\\w]+ to be in the range \\(2 - 3\\), got 1"),
|
||||
},
|
||||
{
|
||||
val: "1",
|
||||
f: IntBetween(2, 3),
|
||||
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be int"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidationStringInSlice(t *testing.T) {
|
||||
runTestCases(t, []testCase{
|
||||
{
|
||||
val: "ValidValue",
|
||||
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
||||
},
|
||||
// ignore case
|
||||
{
|
||||
val: "VALIDVALUE",
|
||||
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, true),
|
||||
},
|
||||
{
|
||||
val: "VALIDVALUE",
|
||||
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
||||
expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got VALIDVALUE"),
|
||||
},
|
||||
{
|
||||
val: "InvalidValue",
|
||||
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
||||
expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got InvalidValue"),
|
||||
},
|
||||
{
|
||||
val: 1,
|
||||
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
||||
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func runTestCases(t *testing.T, cases []testCase) {
|
||||
matchErr := func(errs []error, r *regexp.Regexp) bool {
|
||||
// err must match one provided
|
||||
for _, err := range errs {
|
||||
if r.MatchString(err.Error()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
_, errs := tc.f(tc.val, "test_property")
|
||||
|
||||
if len(errs) == 0 && tc.expectedErr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(errs) != 0 && tc.expectedErr == nil {
|
||||
t.Fatalf("expected test case %d to produce no errors, got %v", i, errs)
|
||||
}
|
||||
|
||||
if !matchErr(errs, tc.expectedErr) {
|
||||
t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/test"
|
||||
"github.com/hashicorp/terraform/internal/legacy/builtin/providers/test"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func main() {
|
|
@ -3,7 +3,7 @@ package test
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testDataSource() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func providerLabelDataSource() *schema.Resource {
|
|
@ -1,8 +1,8 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/terraform"
|
||||
)
|
||||
|
||||
func Provider() terraform.ResourceProvider {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResource() *schema.Resource {
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceComputedSet() *schema.Resource {
|
|
@ -3,7 +3,7 @@ package test
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceConfigMode() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceDefaults() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceDeprecated() *schema.Resource {
|
|
@ -5,7 +5,7 @@ import (
|
|||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceDiffSuppress() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceForceNew() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
// This is a test resource to help reproduce GH-12183. This issue came up
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceList() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceListSet() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceMap() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceNested() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceNestedId() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceNestedSet() *schema.Resource {
|
|
@ -3,7 +3,7 @@ package test
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceProviderMeta() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceRequiredMin() *schema.Resource {
|
|
@ -1,7 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceSignal() *schema.Resource {
|
|
@ -7,7 +7,7 @@ import (
|
|||
"math/rand"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceStateFunc() *schema.Resource {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||
)
|
||||
|
||||
func testResourceTimeout() *schema.Resource {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue